Re[9]: DirectoryInfo
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.09.05 22:06
Оценка: 2 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

VD>>Дык используй строковый вариант. Он экономичнее.


PD>Не понял, что за строковый вариант.


Directory.GetFiles()

Возвращающая список имен файлов.

PD>Если бы только тут. Похоже, это их принципиальный подход. Шрифты хочешь перечислить — получи от InstalledFontCollection все семейства. Их, конечно, 100,000 не будет


Конечно. Так о чем тогда разговор?

PD>Во-во. Именно — если ее хватает. А в результате имеем соответствующие требования по памяти. Чего уж тут говорить, если для реализации команды dir *.* требуется 57 Мбайт!


Если у тебя проблема с памятью, то дотнет тебе просто противопаказан. Он жрет память довольно сильно. Причем сам по себе фрэймворк. Приложение может даже и не особо ее занимать. Так на машинах с большим объемом памяти (более 512 метров) второй фрэймворк банально на всякий пожарный занимает с самого начала 30 метров виртуалки. Не факт что она будет реально назначена (под нее будет отведена физическая память), но тем не менее.

Собственно что трепаться? Делаем примитивный эксперемент. Создаем пустое приложение. Ну, что-то вроде этого:
using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start...");
        
        DateTime start = DateTime.Now;

        int count = 0;

        Console.WriteLine("Done " + (DateTime.Now - start) 
            + " file count = " + count + "...");
        Console.ReadLine();
    }
}

Запускаем его и смотрим с помощью Procexp (sysinternals-овским) сколько это чудо сожрет памяти. У меня на машине плучается:
Peak working Set     4 346 K
Peak Private Bytes   7 088 K

Теперь закрываем приложение, дописываем его следующим образом:
using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start...");
        
        DateTime start = DateTime.Now;

        int count = 0;
        foreach (string s in Directory.GetFiles(@"F:\Backup\rsdn.ru\WWW\Offline\Forum"))
            count++;

        Console.WriteLine("Done " + (DateTime.Now - start) 
            + " file count = " + count + "...");
        Console.ReadLine();
    }
}

и натравливаем на каталог содержащий ~19 000 файлов. F:\Backup\rsdn.ru\WWW\Offline\Forum собсвтенно — это каталог с HTML-файлами RSDN Offline что кладется на наш CD.
Далее запускаем этот тест и снова смотрим расклд памяти:
Peak working Set     8 108 K
Peak Private Bytes  10 400 K

Много? Да не мало. Вот только и не смертельно учитывая что каталог не маленький.
Теперь смотрим на скорость. Да первый запуск долний. Но он не станет меньше от того что программа будет переписана на С. А второй запуск практически молниеносен:
Start...
Done 00:00:00.1093715 file count = 19409...

Обрати внимание на количество файлов.

Да, не спорю. Моя машина быстрая. Возможно твоя в 10 раз медленее и ты будешь ждать 1 секунду, но не как не две минуты про которые ты тут рассказывал. Так что если у тебя перебор файлов занимает 2 минуты, то или это время уходит на чтение с диска, или ты еще что-то делашь.

Продолжаем исследования...
Теперь переключимся на закладку ".NET" и выберем в комбе значение просмотр управяемой памяти:

Что же мы видим? Под память всех хипов домена приложения было отведено всего 3 метра! Под второе поколение (где реально и живет память) вообще выделено 12 байт. А реальная память занята только в нулевом поколении и обхем памяти составляет всего 1.3 метра. Это как раз то что нужно для выделения 19 тысяч строк.

Теперь еще раз изменим код и попробуем подсчитать сколько же памяти требует массв со строками. Каждый дотнетый объект жрет 8 байт на внутренние структуры плюс строки в дотнете юникодные, так что формула будет такая:
size += 8 + s.Length * 2;

Вставим ее в код:
using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Start...");
        
        DateTime start = DateTime.Now;

        int size = 0;
        foreach (string s in Directory.GetFiles(@"F:\Backup\rsdn.ru\WWW\Offline\Forum"))
            size += 8 + s.Length * 2;

        Console.WriteLine("Done " + (DateTime.Now - start) 
            + " количество байт требуемое для хранения строк = " + size + "...");
        Console.ReadLine();
    }
}

запустим... и получим результат:
Start...
Done 00:00:00.1093715 количество байт требуемое для хранения строк = 2106022...

Итого на хранение строк требуется 2.1 метра. Забавно, что после цикла дотнет умудряется уже освободить ее часть.



Вот теперь можно поговорить о памяти и о ужасно не эффективных алгоритмах. Как видишь в самом худшем случае мы теряем около 2 метров. Не спорю, 20 лет подряд за такой расход памяти просто убили бы. Но 20 лет назад обработка 19 тысячи файлов никак не могла бы занять одну десятую секунды. Так что мы платим за скорость и удобство самым дешевым ресурсом — памятью. А те кто не в силах расстаться со своими догмами может и дальше трахаться со всеми этими АПИ-шными наворотами.

Можно ли написать код чтения файлов более оптимально? Несомненно. Воспользуйся паттерном итератор и получишь мало чем отличимое от АПИ-шного решение. Но намногь более удобное в использовании.

Почему это не сделали? Нет необходимости. И так более чем пригодно для использования. Те сотые доли процента программистов которых не устраивает данное решение вольны написать совбстенное решение с использованием более подходящих для них паттернов. Времени на это нужно минимум. В основном оно уйдет на описание струтур АПИ. Которые, кстати, можно взять на www.pinvoke.net.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.