Как сделать свою программу быстрой
 
10.06.2003
Андрей Гапанович


 
<< стр. 1
стр. 2

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

- ProDelphi (www.prodelphi.de),
- Delphi Profiler (www.axiomati.demon.co.uk),
- GpProfile (www.eccentrica. org/gabr/gpprofile/gpprofile.htm).
На мой взгляд, GpProfile является наилучшим — это open source-проект, и при желании можно скачать исходные тексты и доработать под свои нужды.

GpProfile представляет собой модуль gpprof.pas и исполняемый файл gpprof.exe. Прежде всего, необходимо открыть проект, «узкие» места которого мы будем искать. GpProfile просканирует все модули вашего проекта и отобразит все их функции, процедуры и методы. Далее от вас требуется отметить те процедуры и функции, которые вы собираетесь исследовать (рис. 1). Можно, конечно, выбрать все, но тогда будет гораздо сложнее разбираться в результатах тестирования. Если вы хотите улучшить производительность какого-то определенного действия пользователя, то отметьте именно те функции, которые связаны с этим действием. Следующим шагом следует выбрать пункт меню Project/Instrument. При этом GpProfile изменит модули вашего проекта, в которых встречаются отмеченные функции, создав при этом резервную копию исходного кода. Изменения сводятся к следующему:
- В секции uses добавляется строка {>>GpProfile U} GpProf, {GpProfile U>>}, подключая модуль, который упоминался ранее.

- В начале каждой процедуры, функции или метода добавляется {>>GpProfile} ProfilerEnterProc(1); try {GpProfile>>}. С помощью вызова ProfilerEnterProc засекается начало выполнения процедуры. В качестве аргумента выступает номер, уникальный для каждой функции.

- В конце каждой функции добавляется {>>GpProfile} finally ProfilerExitProc(6); end; {GpProfile>>}. Здесь засекается время завершения работы функции.

После завершения работы программы накопленная информация обрабатывается и показывается в окне exe-модуля GpProfile (рис. 2). Статистику можно анализировать по модулям, классам, процедурам. Для каждой процедуры можно узнать следующие статистические данные:

- Time — общее время, в течение которого работала процедура;
- w/Child — общее время, в течение которого работала процедура, включая вызовы дочерних процедур;
- Calls — количество вызовов процедуры;
- Min/Call, Max/Call, Avg/Call — минимальное, максимальное и среднее время одного выполнения процедуры;
- % Time — процент времени работы процедуры от общего времени работы отмеченных процедур.

Теперь у нас есть информация о том, какие участки кода работают долго и какие вызываются чаще других. Оптимизации этих процедур стоит уделить наибольшее внимание. Следует отметить недостаток этого профайлера — нельзя размещать метки в произвольных местах, поэтому если вы хотите подробнее исследовать работу достаточно длинной процедуры, придется разбить ее на более мелкие. После изменения кода запускайте профайлер снова, смотрите, какая процедура теперь «съедает» больше всего времени, оптимизируйте ее и так до тех пор, пока не добьетесь удовлетворительной скорости. Для каждой программы существует определенный предел, преодолеть который не удастся, применяя даже самые изощренные способы оптимизации. Но все же с помощью описанной методики обычно удается существенно повысить скорость работы, а иногда и вовсе избавиться от медлительности программы. После завершения оптимизации уберите метки, которые вставил в код GpProfile. Для этого выберите пункт меню Project/Remove instrumentation, и ваш код примет исходный вид. Но изменения, произведенные вами для повышения быстродействия, конечно же, останутся.

Девять полезных советов

1 При написании кода программ старайтесь избегать процедур, состоящих из сотен строк. Практически всегда в них можно выделить блоки, которые лучше оформить в виде отдельной процедуры. Возможно, позже вы ею даже воспользуетесь где-то в другом месте. Не говоря уже о том, что это повышает понимание программы и вами, и другими программистами. К тому же так проще искать «узкие» места в вашей программе.
2 Пользуйтесь оператором case вместо многократных if… else if… Во втором варианте компилятор будет выполнять проверку условия столько раз, сколько у вас вариантов. В первом проверка выполняется лишь однажды.
3 Некоторые действия могут быть довольно продолжительными, поэтому выносите за рамки цикла все, что можно выполнить вне его, чтобы избежать большого числа повторений внутри цикла.
4 В циклах типа for нужно стараться, чтобы значение счетчика уменьшалось до нуля, а не наоборот — начиналось с нуля. Это связано с особенностями процессора. Сравнение с нулем выполняется гораздо быстрее, чем с другим числом.
5 Старайтесь пользоваться типом Variant только при необходимости. Операции над этим типом сложнее, чем, например, над Integer или String.
6 Не используйте компонент TTreeView для хранения древовидных структур данных — он работает очень медленно и предназначен только для отображения. Когда я заменил этот компонент самодельным деревом на основе TList, программа стала работать быстрее примерно в сто раз.
7 Не пытайтесь приспособить RichEdit для подсветки синтаксиса. Он для этого не предназначен и работает в данном случае очень медленно.
8 Сохранение и загрузка свойств компонентов с помощью методов ReadComponent и WriteComponent работает довольно медленно, поэтому по возможности старайтесь сохранять и восстанавливать состояние программы между сеансами при помощи других способов.
9 Если вы заранее знаете, что в списке для поиска будет много элементов, постарайтесь отсортировать его и применять бинарный поиск вместо линейного.



 
<< стр. 1
стр. 2

<<Двойной стандарт
Все материалы номера
События >>