» Оптимизация Delphi кода
8 Июнь 2010 – 19:09Сегодня решил заняться оптимизацией своего антивируса (да-да, я извращенный вирусолог, имеющий свой антивирус). Поглядел на код и децл ужаснулся: куча лишних операций, присваиваний, сложные действия и прочая лабудень.. Ради интереса запустил сканер. При таком неоптимизированном коде vоя программка прогналась по папке за 5:09 минут. Ок, взялся за оптимизацию.. Привел в порядок типы переменных (убрал лишние конвертирования), поменял циклы (у меня они были в стиле for i:=0 to Length(s) do), заменил операции div 2 на shr 1, поубирал лишние проверки и вообще ненужный код.. Компилируем, тестируем. Опа, та же папочка прогналась за 4:51 минуту. Почти 20 секунд разницы, неплохо так
Буду работать дальше…
P.S. Оптимизация рулит!
14 коменнтов to “» Оптимизация Delphi кода”
Привет
Удивился нашёт:
i:=0 to Length(s) do
Ето что, шитается кривым способом ?
А что тогда правильнее ???
Автор: Janex от июня 9, 2010
Привет. Да, такому учат все учебники и преподователи, но это с точки зрения оптимизации крайне не верно! Потому как для каждой итерации цикла будет дополнительно вызываться функция Length, что замедлит выполнение программы. Открой дизассемблером код и проверь для двух случаев:
1) с Length
2) с переменной, которой ранее был присвоен размер
Увидишь разницу.
Правильный вариант: до начала цикла занести в переменную Len (как пример) значение Length(s) и делать цикл от x до Len. В таком случае лишних действий выполняться не будет
Автор: Булай Никита от июня 9, 2010
Сделал проверку, сколько раз быдет вызываться функция Length в цикле for … to Length() do
Ответ: один раз. Так что не знаю, почему это плохо ….
//————
function TForm1.MyLength(const AStr: string): integer;
begin
Result:=Length(AStr);
Memo.Lines.Add(’#');
end;
//————
procedure TForm1.TestButtonClick(Sender: TObject);
var
i : integer;
begin
for i:=1 to MyLength(’Hallo’) do
Memo.Lines.Add(’i=’+IntToStr(i));
end;
//————
#
i=1
i=2
i=3
i=4
i=5
Автор: Johnny от июня 9, 2010
Никита, ты не прав.
Сделай простейший тест:
var
s: string;
i: integer;
begin
s := ‘abcdefghij’;
for i := 1 to length(s) do begin
write(s[1]);
SetLength(s, 5);
end;
readln;
end.
Сколько по-твоему буквочек “а” появится на экране?
Правильный ответ - 10. Потому что граничное условие в цикле for не пересчитывается.
Автор: Kryvich от июня 9, 2010
на самом деле есть профайлеры для Дельфей. Бесплатный и достаточно удобный SamplingProfiler (http://delphitools.info/).
1. Пересобираем свой проект со включенной отладочной информацией.
2. Запускаем программу под профайлером, работаем в ней некоторое время.
3. После этого останавливаем программу и профайлер показывает какие строки нашей программы выполнялись чаще всего и откуда был вызов этих строк.
В этом пофайлере отдельные алгоритмы сбора статистики для однопоточной программы и для многопоточной!
Автор: imageman от июня 9, 2010
Я бы не стал так утверждать
Вы ничего не поняли. Ваши тесты вообще ничего не показывают. ПРосто выводится значение счетчика i. Так а при чем тут счетчик? Я говорю про машинный код, который сгенерит Delphi. Его будет больше и много лишнего.
Ладно, не верите мне - читаем ОБЯЗАТЕЛЬНО статью “Написание оптимального кода под Delphi” на сайте DelphiMaster.ru, надеюсь, что хотя бы ему вы поверите. Кстати цитирую текст оттуда:
Автор: Булай Никита от июня 9, 2010
Ребята, учите матчасть, читайте официальные help’ы!
Оптимизатор старых версий Delphi (точно не помню до какой версии, толи до Delphi 5, то ли до Delphi 6 включительно) действительно будет вычислять в цикле for выражение to в каждой итерации цикла.
В более новых версиях Delphi, при включённой оптимизации, такого происходить не будет (спасибо разработчикам).
И не надо слепо верить статьям, тем более в той же статье на DelphiMaster есть примечание: “Внимание! В статье имеются неточности и некорректные утверждения.”
Автор: sw от июня 9, 2010
Точно будет больше кода? Это ж легко проверить, добавим оба варианта, и посмотрим окошко Entire CPU (Ctrl-Alt-C):
var
s: string;
i, len: integer;
begin
s := ‘abcdefghij’;
for i := 1 to length(s) do begin
write(s[1]);
SetLength(s, 5);
end;
writeln;
s := ‘abcdefghij’;
len := length(s);
for i := 1 to len do begin
write(s[1]);
SetLength(s, 5);
end;
writeln;
readln;
end.
Кстати, не забудь включить оптимизацию, и отключить Range Checking / Overflow checking в опциях компилятора.
И да, какой у тебя Delphi? У меня 2007.
Автор: Kryvich от июня 9, 2010
У меня 2006. Да, эта оптимизация верна для старых версий (и все же не помешала бы и для новых). Много кто сейчас все еще юзает 6 делфи. Но кто сказал, что я ограничился только этим методом оптимизации? Читайте и еще раз читайте
Автор: Булай Никита от июня 9, 2010
Булай Никита, извини, что я тебя поливаю грязькой, но считаю, что это надо сделать.
Опыта у тебя _мало_, теже оптимизации типа “заменил div 2 на shr 1″ - это, по большому счёту, забота комилятора, а не программиста.
“поубирал лишние проверки и вообще ненужный код” - а это вот уже твоя проблема, что ты так раньше писал, и то, что ты пересматриваешь старый код, и признаёшь, что раньше писал всякую хрень - это хорошо..
В любом случае, тебе любой Программист скажет, что оптимизация путём подстройки своего кода под компилятор - это не оптимизация (это, кстати, ещё надо в себе выработать - писать код так, чтобы потом не делать такой “оптимизаци”). В твоём случае ты получил прирост производительности 0,06%, это смешно.
Настоящая оптимизация заключается в пересмотре алгоритмов (твоих и тех, что ты используешь).
К примеру, недавно у меня был случай. Подпрограмма строила дерево по данным, считываемым из БД. В общем случае - до 100 строк, дерево строилось за доли секунды. Но в один прекрасный день из БД пришло 80 тысяч строк - дерево так и не построилось (пользователь ждал минут 15 - недождался).
Оптимизацию я провёл в два этапа (с у чётом того, что SQL-запрос менять нельзя и данные из БД могут приходить в хаотичном порядке). В первый раз концептуально изменил алгоритм подпрограммы (дерево в 80 тыс. строк строилось уже где-то за минуту), в следующий раз - изменил структуру данных в подпрограмме. Теперь такое дерево строится секунд за шесть.
Всего два с половиной часа работы и таааакой прирост производительности
Вот тут я могу смело сказать, что “оптимизация рулит”
Автор: sw от июня 9, 2010
Однако он этого не делает
А где я сказал, что это не моя проблема? Речь в заметке идет не про то, что Delphi плох в оптимизации и так далее, а о том, что код иногда надо проверять и по возможности оптимизировать. Не верно поняли смысл
Вот это скорее говорит о том, что “Опыта у тебя _мало_, “, а не у меня
Без обид. Любой прирост, даже самый несущественный, всегда может принести ту или иную пользу/выгоду. Вспомни летние олимпийские игры, когда при поднятии штанги Белорус почти стал чемпионом, но какой-то китаец, подняв столько же - выйграл! А знаешь почему? Потому что его вес был меньше на 200 грамм!!! Думаю, что идею ты понял, если нет - не надо писать об этом
Спасибо, я знаю, что такое оптимизация и в чем она заключается
Молодец, уважаю.
А эт делать не надо
P.S. Не этом предлагаю закончить спор о способах оптимизации, потому какк мораль сей басни такова: старайтесь писать оптимальный код
Автор: Булай Никита от июня 9, 2010
sw “прирост производительности 0,06%, это смешно”
Смешнее как Вы насчитали 0.06% тогда как тут ~6%?
Во-вторых не надо тут приводить себя в пример. Может у Вас код изначально был ужасно кривой и Вы подумав над алгоритмом добились многократного увеличения производительности, тогда как у автора код был изначально нормальным и он применив дополнительную оптимизацию добился еще 6% увеличения производительности?
А насчет кода типа
for i := 0 to Memo1.Lines.Count или Length(s)
уже давно все знают что встроенный оптимизатор такое за программистами убирает.
imageman спасибо за ссылку на этот профайлер, раньше про него ничего не знал. Юзал GProfile
Автор: Дмитрий от июня 9, 2010
delphi6 - справка по циклу for:
The difference between this construction and the for…to statement is that the while loop re-evaluates finalValue before each iteration. This can result in noticeably slower performance if finalValue is a complex expression, and it also means that changes to the value of finalValue within statement can affect execution of the loop.
в 6-ой дельфе соответсвенно Length в for вызывается только один раз, независимо от оптимизации (проверил)
Автор: jk от июня 9, 2010
Сегодня узнал, что если заменить:
for i:= 0 to 200 do
на for i:=200 downto 0 do
то можно выиграть в оптимизации. Процессору не придется делать одной лишней операции.
Автор: Булай Никита от Сен 1, 2010