Расчет размера файла каталога - как это сделать быстрее? - программирование
Подтвердить что ты не робот

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

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

Я пробовал еще один способ сделать это: Использование FSO (obj.GetFolder(path).Size). В обоих этих подходах не так много разницы во времени.

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

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

4b9b3361

Ответ 1

Если немного поиграть с ней, пытаясь ее рассчитать, и удивительно - она ​​ускорилась здесь на моей машине (до трех раз на квадроцикле), не знаю, действительно ли она во всех случаях, но дайте ей попробуйте...

Код .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;
    }

Ответ 2

Жесткие диски - интересный зверь - последовательный доступ (например, чтение большого смежного файла) - супер-zippy, рисунок 80 мегабайт/сек. однако произвольный доступ происходит очень медленно. это то, с чем вы сталкиваетесь - рекурсия в папки не читает много (с точки зрения количества) данных, но потребует много случайных чтений. Причина, по которой вы видите zippy perf второй, заключается в том, что MFT все еще находится в ОЗУ (вы верны при мысли о кешировании)

Лучший механизм, который я видел для этого, - это сканировать MFT самостоятельно. Идея заключается в том, что вы читаете и анализируете MFT в одном линейном проходе, строя информацию, необходимую вам по ходу дела. Конечный результат будет намного ближе к 15 секундам на HD, который очень заполнен.

какое-то хорошее чтение: NTFSInfo.exe - http://technet.microsoft.com/en-us/sysinternals/bb897424.aspx Windows Internals - http://www.amazon.com/Windows%C2%AE-Internals-Including-Windows-PRO-Developer/dp/0735625301/ref= sr_1_1 т.е. = UTF8 &? s = книги & QID = 1277085832 & ср = 8-1

FWIW: этот метод очень сложный, так как в Windows (или какой-либо ОС, о которой я знаю) действительно нет отличного способа сделать это - проблема в том, что нужно выяснить, какие папки/файлы необходимы требует большого движения головы на диске. Для Microsoft было бы очень сложно создать общее решение проблемы, которую вы описываете.

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

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

Предложения

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

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

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

Ответ 6

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

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

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

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

Ответ 7

Я отказался от реализации .NET(по соображениям производительности) и использовал функцию Native 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;
}