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>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: DirectoryInfo
От: Pavel Dvorkin Россия  
Дата: 27.09.05 04:48
Оценка:
Здравствуйте, VladD2, Вы писали:


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


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


Да все о том же

VD>Если у тебя проблема с памятью, то дотнет тебе просто противопаказан.


768 Мб. PIV-1600.

VD>Собственно что трепаться? Делаем примитивный эксперемент. Создаем пустое приложение.


<skipped>

VD>Почему это не сделали? Нет необходимости. И так более чем пригодно для использования. Те сотые доли процента программистов которых не устраивает данное решение вольны написать совбстенное решение с использованием более подходящих для них паттернов. Времени на это нужно минимум. В основном оно уйдет на описание струтур АПИ. Которые, кстати, можно взять на www.pinvoke.net.


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

>Он (фрэймворк- PD) жрет память довольно сильно. Причем сам по себе фрэймворк.


А не является ли, в частности причиной того, что он жрет память, то, что и он написан с применением такого же подхода, когда 2 Мб больше, 2 меньше — наплевать. Там наплевать, здесь наплевать, а в итоге — результат.
Очень прошу тебя — не отвечай сейчас на это мое письмо. Потому как я не удержусь и тоже отвечу , а мне не хотелось бы здесь и сейчас эту дискуссию разворачивать — ну хотя бы потому, что здесь ей просто не место. Я постараюсь в ближайшее время сформулировать свои соображения на эту тему более конкретно (наверное, в Философии), буду рад там выслушать твои замечания.
With best regards
Pavel Dvorkin
Re[12]: DirectoryInfo
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.09.05 05:52
Оценка:
Здравствуйте, Andrbig, Вы писали:

PD>>Можно, конечно. Включить в MyDirectoryInfo экземпляр DirectoryInfo, переписать все его методы, вызывая те, что в нем есть, из него, а свои реализуя сам. Можно... Только что-то слишком много работы для такого пустяка


A>Да не так уж и много там методов. Пара часов интенсивной работы и все.


Главное не ясно зачем вообще переписывать DirectoryInfo если оно получается по имени.

Все что нужно сделать это создать класс который будет предоставлять методы перечисления возвращающие имена файлов. Далее создать по имени DirectoryInfo ничего не стоит.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: DirectoryInfo
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.09.05 05:55
Оценка: 38 (1)
Здравствуйте, Sinclair, Вы писали:

S>Я думаю, самое разумное — вызывать пользовательский делегат.


В таких случаях немеряно рулят итераторы.

S>Напрашивается решение с IEnumerable — оно позволит очень легко повторно использовать существующий код, который работает с FileInfo[]. Но я подозреваю некоторые трудности с диспозом.


Не нужно вообще возвращать FileInfo. Нужно возращать строку:

public static IEnumerable<string> GetFiles(string path)
{
    string name;
    while (...)
    {
        ...
        // получаем файлы по одному и возвращаем их
        yalde return name;
    }
}


S>По идее, надо гарантировать своевременность вызова FindClose. Но стандартный оператор foreach, насколько мне известно, использует только IEnumerable/IEnumerator.


Очень даже делает. К тому же если что финалайзер поможет.

S> Поэтому банальной реализации IDisposable вместе с IEnumerator недостаточно.


IEnumerator<T> в обязательном порядке наследуется от IDisposable!

S>Кстати, вопрос к гурам: нельзя ли это победить во втором дотнете?


Проблем небыло и в первом. Если объект реализует IDisposable, foreach всегда его дергал. Ну, а во втором... см. выше.

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

S>Когда будет вызван код в finally?


В конце выполнения кода. Короче, из диспоза в который будет перенесена обратотка из finally.

S> При каждом выходе по yield return?


Нет.

S>По окончанию енумерирования?


Да.

S> Что, если енумерирование будет прервано досрочно?


Будет вызван finally. Корече, все будет ОК. Итераторы — это почти полноценное континиэйшон. Так что такие вещи он обработает.

Короче, проще самому написать чем объясняь:
Перебор файлов с использованием FindFirstFile/FindNextFile и
Автор: VladD2
Дата: 27.09.05
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: DirectoryInfo
От: Andrbig  
Дата: 27.09.05 06:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Главное не ясно зачем вообще переписывать DirectoryInfo если оно получается по имени.


Охота пуще неволи. Ну охота ему не читать весь каталог в массив (даже и строк), а перебирать по одному.

Кстати, Павел! Если ты напишешь такой класс, а главное — прогонишь в своих условиях, расскажи потом насколько это было быстрее и меньше по памяти. Интересно.

VD>Все что нужно сделать это создать класс который будет предоставлять методы перечисления возвращающие имена файлов. Далее создать по имени DirectoryInfo ничего не стоит.


Это да. Вопрос в другом. Где поставить FindClose? Похоже, придется связываться с IDisposable?
Re[11]: DirectoryInfo
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.09.05 06:05
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Вопрос же вот в чем — мне кажется, что начал культивироваться стиль программирования, который на первое место ставит что угодно, но не эффективность. И вот это меня настораживает.


А производительность и не должна быть на первом месте. Она должна быть достаточной. А на первом месте должны быть удобство и простота.

PD>Ты пишешь


PD>А не является ли, в частности причиной того, что он жрет память, то, что и он написан с применением такого же подхода, когда 2 Мб больше, 2 меньше — наплевать. Там наплевать, здесь наплевать, а в итоге — результат.


Ага. Именно что в итоге — результат! И очень даже приличный.

Вот, ради хохм реализовал тебе перебор файлов в линивом режиме:
Перебор файлов с использованием FindFirstFile/FindNextFile и
Автор: VladD2
Дата: 27.09.05
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[14]: DirectoryInfo
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.09.05 06:12
Оценка:
Здравствуйте, Andrbig, Вы писали:

A>Кстати, Павел!


Кстати, я Влад.

--------------------
— Скажите вы случайно не еврей?
— Вы знаете? Соврешенно случайно.


A> Если ты напишешь такой класс, а главное — прогонишь в своих условиях, расскажи потом насколько это было быстрее и меньше по памяти. Интересно.


Вот только только грохнул огромный каталог. Так что пока что прогнать не на чем. Да и спать охода. Код лежит тут
Автор: VladD2
Дата: 27.09.05
. Пробуйте сами.

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

A>Это да. Вопрос в другом. Где поставить FindClose?


В конце! Короче, код я привел. Диспоз вроде вызывается.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.