Более эффективный метод получения размера каталога - PullRequest
8 голосов
/ 23 марта 2012

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

static string GetDirectorySize(string parentDir)
{
    long totalFileSize = 0;

    string[] dirFiles = Directory.GetFiles(parentDir, "*.*", 
                            System.IO.SearchOption.AllDirectories);

    foreach (string fileName in dirFiles)
    {
        // Use FileInfo to get length of each file.
        FileInfo info = new FileInfo(fileName);
        totalFileSize = totalFileSize + info.Length;
    }
    return String.Format(new FileSizeFormatProvider(), "{0:fs}", totalFileSize);
}

Это поиск во всех подкаталогах дляпуть аргумента, поэтому массив dirFiles становится довольно большим.Есть ли лучший способ сделать это?Я искал вокруг, но пока ничего не нашел.

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

Ответы [ 5 ]

25 голосов
/ 23 марта 2012

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

Я предлагаю вам использовать DirectoryInfo.GetFiles, что даст вам FileInfo объекты напрямую.Эти объекты предварительно заполнены своей длиной.

В .NET 4 вы также можете использовать метод EnumerateFiles, который вернет вам ленивый IEnumable.

12 голосов
/ 23 марта 2012

Попробуйте

        DirectoryInfo DirInfo = new DirectoryInfo(@"C:\DataLoad\");
        Stopwatch sw = new Stopwatch();
        try
        {
            sw.Start();
            Int64 ttl = 0;
            Int32 fileCount = 0;
            foreach (FileInfo fi in DirInfo.EnumerateFiles("*", SearchOption.AllDirectories))
            {
                ttl += fi.Length;
                fileCount++;
            }
            sw.Stop();
            Debug.WriteLine(sw.ElapsedMilliseconds.ToString() + " " + fileCount.ToString());
        }
        catch (Exception Ex)
        {
            Debug.WriteLine(Ex.ToString());
        }

Это было 700 000 за 70 секунд на настольном компьютере NON-RAID P4.Так, как 10000 в секунду.На сервере класса машина должна набрать 100 000+ / сек. Просто.

Как сказал usr (+1), EnumerateFile предварительно заполнен длиной.

11 голосов
/ 23 марта 2012

Это более загадочно, но для выполнения 10k понадобилось около 2 секунд.

    public static long GetDirectorySize(string parentDirectory)
    {
        return new DirectoryInfo(parentDirectory).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
    }
4 голосов
/ 23 марта 2012

Вы можете немного ускорить свою функцию, используя EnumerateFiles() вместо GetFiles(). По крайней мере, вы не загрузите полный список в память.

Если этого недостаточно, вы должны сделать вашу функцию более сложной, используя потоки (один поток на каталог слишком много, но нет общего правила).
Вы можете использовать фиксированное количество потоков, которое просматривает каталоги из очереди, каждый поток вычисляет размер каталога и добавляет к общему. Что-то вроде:

  • Получить список всех каталогов (не файлов).
  • Создать N потоков (например, по одному на ядро).
  • Каждый поток просматривает каталог и вычисляет размер.
  • Если в очереди нет другого каталога, поток заканчивается.
  • Если в очереди есть каталог, он рассчитывает его размер и т. Д.
  • Функция завершается, когда завершаются все потоки.

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

0 голосов
/ 22 августа 2016
long length = Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...