Расчет размера файла каталога-как сделать это быстрее?
используя C#, я нахожу общий размер каталога. Логика такова: получить файлы внутри папки. Суммируйте общий размер. Найдите, есть ли подкаталоги. Затем выполните рекурсивный поиск.
я попробовал еще один способ сделать это: используя FSO (obj.GetFolder(path).Size
). В обоих этих подходах разница во времени невелика.
теперь проблема в том, что у меня есть десятки тысяч файлов в определенной папке, и это занимает по крайней мере 2 минуты, чтобы найти размер папки. Кроме того, если я снова запустите программу, это происходит очень быстро (5 секунд). Я думаю, что windows кэширует размеры файлов.
есть ли способ уменьшить время, затраченное при первом запуске программы??
7 ответов
Если возился с ним некоторое время, пытаясь распараллелить его, и удивительно - он ускорился здесь на моей машине (до 3 раз на quadcore), не знаю, действительно ли это во всех случаях, но попробуйте...
.NET4.0 код (или используйте 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;
}
жесткие диски-интересный зверь-последовательный доступ (например, чтение большого смежного файла) - super zippy, рисунок 80megabytes/sec. однако случайный доступ очень медленный. это то, на что вы натыкаетесь - рекурсия в папки не будет читать много (с точки зрения количества) данных, но потребует много случайных чтений. Причина, по которой вы видите zippy perf во второй раз, заключается в том, что 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 было бы очень сложно создать общее решение проблемы, которую вы описывать.
короткий ответ: нет. Способ, которым 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.
Peformance будет страдать используя любой метод при сканировании папки с десятками тысяч файлов.
использование FINDFIRSTFILE API Windows... и findnextfile функция... функции обеспечивают самый быстрый доступ.
из-за маршалинга накладных расходов, даже если вы используете функции Windows API, производительность не будет увеличиваться. Платформа уже обертывает эти функции API, поэтому нет смысла делать это себе.
как вы регулируете результаты для любого метода доступа к файлам определяет производительность вашего приложения. Например, даже если вы используете функции Windows API, обновление списка-это то, где производительность пострадает.
нельзя сравнивать скорость в Проводнике Windows. Из моих экспериментов я считаю, что проводник Windows читает непосредственно из таблицы распределения файлов во многих случаи.
Я знаю, что самый быстрый доступ к файловой системе-это . Нельзя сравнивать производительность с этой командой. Он определенно читает непосредственно из таблицы распределения файлов (возможно, используя BIOS).
Да, операционная система кэширует доступ к файлам.
предложения
интересно, если
BackupRead
поможет кейс?что, если вы раскошелитесь на DIR и захватите, а затем проанализируете его выход? (На самом деле вы не анализируете, потому что каждая строка DIR имеет фиксированную ширину, поэтому это просто вопрос вызова подстроки.)
что, если вы раскошелитесь на
DIR /B > NULL
в фоновом потоке затем запустите свою программу? Во время работы DIR вы получите доступ к кэшированным файлам.
с десятками тысяч файлов, вы не собираетесь выиграть с лобовым нападением. Вам нужно попытаться быть немного более творческим с решением. С таким количеством файлов вы, вероятно, даже найдете, что за время, необходимое для вычисления размера, файлы изменились, и ваши данные уже ошибочны.
Итак, вам нужно переместить нагрузку в другое место. Для меня ответом было бы использовать System.IO.FileSystemWatcher
и напишите код, который отслеживает каталог и обновляет индекс.
запись службы Windows, которая может быть настроена для мониторинга набора каталогов и записи результатов в общий выходной файл, займет совсем немного времени. Вы можете заставить службу пересчитать размеры файлов при запуске, но затем просто отслеживать изменения всякий раз, когда событие Create/Delete/Changed запускается System.IO.FileSystemWatcher
. Преимущество мониторинга каталога заключается в том, что вас интересуют только небольшие изменения, а это означает, что ваши цифры имеют более высокий шанс быть правильным (помните, что все данные устаревшие!)
тогда единственное, на что нужно обратить внимание, - это то, что у вас будет несколько ресурсов, пытающихся получить доступ к результирующему выходному файлу. Так что просто убедитесь, что вы принимаете это во внимание.
Я отказался от реализаций .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;
}