Используя C#, я нахожу общий размер каталога. Логика является этим путем: Получите файлы в папке. Подведите итог общего размера. Найдите, существуют ли подкаталоги. Затем сделайте рекурсивный поиск.
Я попробовал друг друга способ сделать это также: Используя FSO (obj.GetFolder(path).Size
). Нет большой части разницы во времени в обоих этих подходах.
Теперь проблема, у меня есть десятки тысяч файлов в конкретной папке и ее взятии как по крайней мере 2-минутный для нахождения размера папки. Кроме того, если я запускаю программу снова, это происходит очень быстро (5 secs). Я думаю, что окна кэшируют размеры файла.
Есть ли какой-либо способ, которым я могу снизить время, потраченное, когда я запускаю программу в первый раз??
Я немного повозился с ним, пытаясь распараллелить его, и на удивление - он ускорился на моей машине (до 3 раз на четырехъядернике), не знаю, подходит ли он для всех случаев, но попробуйте...
.NET4.0 Code (или используйте 3.5 с TaskParallelLibrary)
private static long DirSize(string sourceDir, bool recurse)
{
long size = 0;
string[] fileEntries = Directory.GetFiles(sourceDir);
foreach (string fileName in fileEntries)
{
Interlocked.Add(ref size, (new FileInfo(fileName)).Length);
}
if (recurse)
{
string[] subdirEntries = Directory.GetDirectories(sourceDir);
Parallel.For<long>(0, subdirEntries.Length, () => 0, (i, loop, subtotal) =>
{
if ((File.GetAttributes(subdirEntries[i]) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
{
subtotal += DirSize(subdirEntries[i], true);
return subtotal;
}
return 0;
},
(x) => Interlocked.Add(ref size, x)
);
}
return size;
}
Короткий ответ - нет. Способ, которым Windows могла бы ускорить вычисление размера каталога, заключался бы в обновлении размера каталога и всех размеров родительских каталогов при каждой записи файла. Однако из-за этого запись в файл будет выполняться медленнее. Поскольку запись файлов выполняется гораздо чаще, чем чтение размеров каталогов, это разумный компромисс.
Я не уверен, какая именно проблема решается, но если это мониторинг файловой системы, возможно, стоит проверить: http://msdn.microsoft.com/en-us/library/system.io. filesystemwatcher.aspx
Я не думаю, что он сильно изменится, но он может работать немного быстрее, если вы используете функции API FindFirstFile
и ] NextFile
, чтобы сделать это.
Я не думаю, что есть действительно быстрый способ сделать это. Для сравнения вы можете попробовать сделать dir / a / x / s> dirlist.txt
и перечислить каталог в проводнике Windows, чтобы увидеть, насколько они быстры, но я думаю, что они будут похожи на FindFirstFile
.
PInvoke содержит пример использования API.
Производительность пострадает при использовании любого метода при сканировании папки с десятками тысяч файлов.
Использование функций Windows API FindFirstFile... и FindNextFile... обеспечивает самый быстрый доступ.
Из-за накладных расходов на маршаллинг, даже если вы используете функции Windows API, производительность не увеличится. Фреймворк уже обертывает эти функции API, поэтому нет смысла делать это самостоятельно.
То, как вы обрабатываете результаты для любого метода доступа к файлам, определяет производительность вашего приложения. Например, даже если вы используете функции API Windows, обновление списка-box - это то место, где производительность пострадает.
Нельзя сравнивать скорость выполнения с проводником Windows. По моим экспериментам, я полагаю, что Windows Explorer во многих случаях считывает данные непосредственно из таблицы распределения файлов.
Я знаю, что самый быстрый доступ к файловой системе - это команда DIR
. Вы не можете сравнивать производительность с этой командой. Она определенно читает непосредственно из таблицы распределения файлов (возможно, с помощью BIOS).
Да, операционная система кэширует доступ к файлам.
Интересно, поможет ли в вашем случае BackupRead
?
Что если вы выполните команду DIR, а затем разберете ее вывод? (На самом деле вы не разбираете, потому что каждая строка DIR имеет фиксированную ширину, так что это просто вопрос вызова substring.)
Что если вы выполните DIR /B > NULL
в фоновом потоке, а затем запустите свою программу? Пока выполняется DIR, вы будете пользоваться кэшированным доступом к файлам.
С десятками тысяч файлов вы не сможете победить, если будете атаковать в лоб. Вам нужно попытаться подойти к решению немного более творчески. С таким количеством файлов вы, вероятно, даже можете обнаружить, что за то время, пока вы рассчитываете размер, файлы изменились, и ваши данные уже неверны.
Значит, вам нужно переместить нагрузку в другое место. Для меня ответом было бы использование System.IO.FileSystemWatcher
и написание некоторого кода, который отслеживает каталог и обновляет индекс.
Написание службы Windows, которая может быть настроена на мониторинг набора каталогов и запись результатов в общий выходной файл, займет совсем немного времени. Вы можете заставить службу пересчитывать размеры файлов при запуске, но затем просто отслеживать изменения каждый раз, когда событие Create/Delete/Changed запускается System.IO.FileSystemWatcher
. Преимущество мониторинга каталога в том, что вас интересуют только небольшие изменения, а это значит, что ваши цифры имеют больше шансов быть верными (помните, что все данные устаревают!)
Тогда единственное, на что следует обратить внимание, это то, что у вас будет несколько ресурсов, пытающихся получить доступ к результирующему выходному файлу. Поэтому просто убедитесь, что вы учли это.
Жесткие диски - интересный зверь - последовательный доступ (например, чтение большого непрерывного файла) очень быстр, скорость составляет 80 мегабайт / сек. однако произвольный доступ очень медленный. это то, с чем вы сталкиваетесь - рекурсивный просмотр папок не будет читать много (с точки зрения количества) данных, но потребует много случайных чтений. Причина, по которой вы видите резкую производительность при втором обходе, заключается в том, что MFT все еще находится в ОЗУ (вы правы в отношении кеширования)
Лучший механизм, который я видел для достижения этого, - это сканирование MFT самостоятельно. Идея состоит в том, что вы читаете и анализируете MFT за один линейный проход, создавая необходимую информацию по ходу дела. Конечный результат будет намного ближе к 15 секундам на очень заполненном HD.
хорошее чтение: NTFSInfo.exe - http://technet.microsoft.com / en-us / sysinternals / bb897424.aspx Внутреннее устройство Windows - http://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-PRO-Developer/dp/0735625301/ref=sr_1_1?ie=UTF8&s=books&qid=1277085832&sr=8- 1
FWIW: этот метод очень сложен, поскольку на самом деле нет отличного способа сделать это в Windows (или любой другой ОС, о которой я знаю) - проблема в том, что процесс определения того, какие папки / файлы необходимо, требует большого движения головы по диску. Microsoft было бы очень сложно найти общее решение описанной вами проблемы.
Я отказался от реализации в .NET (по причинам производительности) и использовал нативную функцию GetFileAttributesEx(...)
Попробуйте так:
[StructLayout(LayoutKind.Sequential)]
public struct WIN32_FILE_ATTRIBUTE_DATA
{
public uint fileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME creationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME lastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME lastWriteTime;
public uint fileSizeHigh;
public uint fileSizeLow;
}
public enum GET_FILEEX_INFO_LEVELS
{
GetFileExInfoStandard,
GetFileExMaxInfoLevel
}
public class NativeMethods {
[DllImport("KERNEL32.dll", CharSet = CharSet.Auto)]
public static extern bool GetFileAttributesEx(string path, GET_FILEEX_INFO_LEVELS level, out WIN32_FILE_ATTRIBUTE_DATA data);
}
Теперь просто сделайте следующее:
WIN32_FILE_ATTRIBUTE_DATA data;
if(NativeMethods.GetFileAttributesEx("[your path]", GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, out data)) {
long size = (data.fileSizeHigh << 32) & data.fileSizeLow;
}