пятница, 28 марта 2008 г.

Немного про оптимизацию программ

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

1. Debug.Write и Debug.WriteLine

Класс Debug является, пожалуй, одним из самых полезных для более-менее крупных программ. Удобен он тем, что нет необходимости как-то заботиться о сохранении отладочных данных – этим занимается сам дотнет. Главная особенность в том, что если проинициализирован «слушатель» отладочной информации – то эта информация гарантированно будет записана в файл в независимости от того в какой части (и в какой сборке) нашей программы будет вызвана функция записи.

Примерная схема работы с этим классом выглядит так:


            Encoding enc = System.Text.Encoding.GetEncoding(1251);
            FileStream fs = new FileStream("log.txt", FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            Debug.Listeners.Add(new TextWriterTraceListener(new StreamWriter(fs, enc)));
            Debug.AutoFlush = true;
            …
            …
            Debug.WriteLine(“Эта строка будет записана в файл log.txt”);
Однако, кроме положительных качеств, данный класс имеет и один, но очень большой, недостаток. А именно – при активной записи отладочных данных скорость выполнения программы резко падает. В частности, в моем личном опыте снижение может достигать 50-100% и более.

Как этого избежать?
Создаем вспомогательную переменную типа StringBuilder (я надеюсь, вы уже знаете, что строки складывать в time-critical алгоритмах – нельзя) и в наиболее активно использующих запись отладочной информации функциях пишем в эту переменную. По окончании работы критичной части программы – сбрасываем данные на диск «одним куском».


2. Особенности выделения памяти.

Собственно, про то, как выделяется память – пишут во всех книжках. Но! Никто не упоминает о неприятных последствиях ее выделения.

В частности, худшее, что можно придумать – это выделять память операцией new внутри цикла или нескольких вложенных циклов, а также внутри функций, вызывающихся в цикле. Такой подход гарантированно приводит к падению производительности на 200-300% и более.

Нет, ну понятно, что память выделять все-таки надо :) Но везде, где это возможно, следует вынести выделение памяти за пределы циклов.
В частности, мой переводчик активно работает со строками – это приводит к необходимости постоянно выделять память под все новые и новые строки. Основная оптимизация, которую я начал активно применять – это активное использование заранее созданных StringBuilder’ов.
Идея следующая – при первом вызове функции проверяется на null специальная вспомогательная переменная, являющаяся членом класса (т.е. внешняя по отношению к функции). Если null – вызывается new StringBuilder. В противном случае – вызывается StringBuilder.Clear(), которая очищает содержимое переменной. В этом случае повторное выделение памяти не производится.
С точки зрения всех книг по проектированию ПО – это очень плохо :) Фактически, большинство time-critical функций имеют дополнительно по 1-2 переменным-членам класса. Кроме того – эти функции нельзя использовать в многопоточном режиме. Однако, если конечная цель – обеспечить высокую производительность для однопоточного алгоритма, то такой подход имеет смысл.

Ну и как заключение.

В результате использования только этих двух методов удалось снизить время работы алгоритма перевода на тестовом тексте с 27 секунд до 6 секунд без изменения самого алгоритма. Это примерно 22% прироста. Замечу, что это суммарное время работы всех частей программы – включая и те, что невозможно оптимизировать такими способами. По отдельным функция прирост производительности составлял порядка от 100 до 300% (в основном – за счет избавления от операции new).

четверг, 27 марта 2008 г.

Немного истории

Совершенно случайно наткнулся на интервью с академиком РАН В.С. Бурцевым. Он рассказывает про отечественные суперкомпьютеры - в частности, про "Эльбрус". Оказывается, разработки были очень и очень эффективны даже не смотря на отсталую элементную базу. А уж идея использования света для передачи информации внутри суперЭВМ в 1994 (точнее в этом году проект уже защитили) - это вообще нечто!

P.S.: К сожалению, как и все у нас в стране данное направление исследований так же было загублено неумелым/глупым руководством...

вторник, 18 марта 2008 г.

Интересная книжка про Linq

LINQ Quickly by Satheesh, N Kumar:

"This book gets you started with LINQ and shows how it will make your programming life easier by making use of new features from the .NET Framework 3.0. This book is split into seven chapters, each of which is dedicated to presenting a feature of LINQ and its usage in real-life scenarios. Language Integrated Query (LINQ) is a new feature in Visual Studio 2008 that extends its query capabilities, using C# and Visual Basic. Visual Studio 2008 comes with LINQ provider assemblies that enable the use of LINQ with data sources such as in-memory collections, SQL relational databases, ADO.NET Datasets, XML documents, etc. In Visual Studio 2008, Visual C# and Visual Basic are the languages that implement the LINQ language extensions. LINQ language extensions use the new standard query operators API, which is the query language for any collection that implements IEnumerable."

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

MP3 CMS - 2

Как оказалось - вчерашний способ запуска сайта сработал только наполовину. Т.е. в режиме "или сканер или сайт". Хотелось, конечно же, и то и то :)

Но сегодня я его все-таки добил :) Оказалось, что достаточно было запустить application pool сайта в классическом режиме не меняя файл web.config - и все сразу заработало. Так что если кто будет переезжать на IIS7 и столкнется с разными проблемами - попробуйте сначала сделать именно это. Авось поможет :)

воскресенье, 16 марта 2008 г.

Mp3 CMS (Веб-разработчикам на заметку)

И все-таки мне это удалось! Я запустил сайт Mp3 CMS по-человечески :)

Собственно, проблема была в том, что некоторые секции в файле web.config "переехали" на другое место. Достаточно было отредактировать файл - и все сразу заработало!