Вычисление размера файла каталога - как сделать его быстрее?

Используя C#, я нахожу общий размер каталога. Логика является этим путем: Получите файлы в папке. Подведите итог общего размера. Найдите, существуют ли подкаталоги. Затем сделайте рекурсивный поиск.

Я попробовал друг друга способ сделать это также: Используя FSO (obj.GetFolder(path).Size). Нет большой части разницы во времени в обоих этих подходах.

Теперь проблема, у меня есть десятки тысяч файлов в конкретной папке и ее взятии как по крайней мере 2-минутный для нахождения размера папки. Кроме того, если я запускаю программу снова, это происходит очень быстро (5 secs). Я думаю, что окна кэшируют размеры файла.

Есть ли какой-либо способ, которым я могу снизить время, потраченное, когда я запускаю программу в первый раз??

19
задан Jey Geethan 5 June 2010 в 06:34
поделиться

7 ответов

Я немного повозился с ним, пытаясь распараллелить его, и на удивление - он ускорился на моей машине (до 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;
    }
34
ответ дан 30 November 2019 в 02:27
поделиться

Короткий ответ - нет. Способ, которым Windows могла бы ускорить вычисление размера каталога, заключался бы в обновлении размера каталога и всех размеров родительских каталогов при каждой записи файла. Однако из-за этого запись в файл будет выполняться медленнее. Поскольку запись файлов выполняется гораздо чаще, чем чтение размеров каталогов, это разумный компромисс.

Я не уверен, какая именно проблема решается, но если это мониторинг файловой системы, возможно, стоит проверить: http://msdn.microsoft.com/en-us/library/system.io. filesystemwatcher.aspx

7
ответ дан 30 November 2019 в 02:27
поделиться

Я не думаю, что он сильно изменится, но он может работать немного быстрее, если вы используете функции API FindFirstFile и ] NextFile , чтобы сделать это.

Я не думаю, что есть действительно быстрый способ сделать это. Для сравнения вы можете попробовать сделать dir / a / x / s> dirlist.txt и перечислить каталог в проводнике Windows, чтобы увидеть, насколько они быстры, но я думаю, что они будут похожи на FindFirstFile .

PInvoke содержит пример использования API.

1
ответ дан 30 November 2019 в 02:27
поделиться

Производительность пострадает при использовании любого метода при сканировании папки с десятками тысяч файлов.

  • Использование функций Windows API FindFirstFile... и FindNextFile... обеспечивает самый быстрый доступ.

  • Из-за накладных расходов на маршаллинг, даже если вы используете функции Windows API, производительность не увеличится. Фреймворк уже обертывает эти функции API, поэтому нет смысла делать это самостоятельно.

  • То, как вы обрабатываете результаты для любого метода доступа к файлам, определяет производительность вашего приложения. Например, даже если вы используете функции API Windows, обновление списка-box - это то место, где производительность пострадает.

  • Нельзя сравнивать скорость выполнения с проводником Windows. По моим экспериментам, я полагаю, что Windows Explorer во многих случаях считывает данные непосредственно из таблицы распределения файлов.

  • Я знаю, что самый быстрый доступ к файловой системе - это команда DIR. Вы не можете сравнивать производительность с этой командой. Она определенно читает непосредственно из таблицы распределения файлов (возможно, с помощью BIOS).

  • Да, операционная система кэширует доступ к файлам.

Предложения

  • Интересно, поможет ли в вашем случае BackupRead?

  • Что если вы выполните команду DIR, а затем разберете ее вывод? (На самом деле вы не разбираете, потому что каждая строка DIR имеет фиксированную ширину, так что это просто вопрос вызова substring.)

  • Что если вы выполните DIR /B > NULL в фоновом потоке, а затем запустите свою программу? Пока выполняется DIR, вы будете пользоваться кэшированным доступом к файлам.

1
ответ дан 30 November 2019 в 02:27
поделиться

С десятками тысяч файлов вы не сможете победить, если будете атаковать в лоб. Вам нужно попытаться подойти к решению немного более творчески. С таким количеством файлов вы, вероятно, даже можете обнаружить, что за то время, пока вы рассчитываете размер, файлы изменились, и ваши данные уже неверны.

Значит, вам нужно переместить нагрузку в другое место. Для меня ответом было бы использование System.IO.FileSystemWatcher и написание некоторого кода, который отслеживает каталог и обновляет индекс.

Написание службы Windows, которая может быть настроена на мониторинг набора каталогов и запись результатов в общий выходной файл, займет совсем немного времени. Вы можете заставить службу пересчитывать размеры файлов при запуске, но затем просто отслеживать изменения каждый раз, когда событие Create/Delete/Changed запускается System.IO.FileSystemWatcher. Преимущество мониторинга каталога в том, что вас интересуют только небольшие изменения, а это значит, что ваши цифры имеют больше шансов быть верными (помните, что все данные устаревают!)

Тогда единственное, на что следует обратить внимание, это то, что у вас будет несколько ресурсов, пытающихся получить доступ к результирующему выходному файлу. Поэтому просто убедитесь, что вы учли это.

0
ответ дан 30 November 2019 в 02:27
поделиться

Жесткие диски - интересный зверь - последовательный доступ (например, чтение большого непрерывного файла) очень быстр, скорость составляет 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 было бы очень сложно найти общее решение описанной вами проблемы.

10
ответ дан 30 November 2019 в 02:27
поделиться

Я отказался от реализации в .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;
}
0
ответ дан 30 November 2019 в 02:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: