» Оптимизация Delphi кода

8 Июнь 2010 – 19:09

Сегодня решил заняться оптимизацией своего антивируса (да-да, я извращенный вирусолог, имеющий свой антивирус). Поглядел на код и децл ужаснулся: куча лишних операций, присваиваний, сложные действия и прочая лабудень.. Ради интереса запустил сканер. При таком неоптимизированном коде vоя программка прогналась по папке за 5:09 минут. Ок, взялся за оптимизацию.. Привел в порядок типы переменных (убрал лишние конвертирования), поменял циклы (у меня они были в стиле for i:=0 to Length(s) do), заменил операции div 2 на shr 1, поубирал лишние проверки и вообще ненужный код.. Компилируем, тестируем. Опа, та же папочка прогналась за 4:51 минуту. Почти 20 секунд разницы, неплохо так :) Буду работать дальше…
P.S. Оптимизация рулит!

  1. 14 коменнтов to “» Оптимизация Delphi кода”

  2. Привет
    Удивился нашёт:
    i:=0 to Length(s) do

    Ето что, шитается кривым способом ?
    А что тогда правильнее ???

    Автор: Janex от июня 9, 2010

  3. Привет. Да, такому учат все учебники и преподователи, но это с точки зрения оптимизации крайне не верно! Потому как для каждой итерации цикла будет дополнительно вызываться функция Length, что замедлит выполнение программы. Открой дизассемблером код и проверь для двух случаев:
    1) с Length
    2) с переменной, которой ранее был присвоен размер
    Увидишь разницу.
    Правильный вариант: до начала цикла занести в переменную Len (как пример) значение Length(s) и делать цикл от x до Len. В таком случае лишних действий выполняться не будет

    Автор: Булай Никита от июня 9, 2010

  4. Сделал проверку, сколько раз быдет вызываться функция 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

  5. Никита, ты не прав.
    Сделай простейший тест:

    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

  6. на самом деле есть профайлеры для Дельфей. Бесплатный и достаточно удобный SamplingProfiler (http://delphitools.info/).

    1. Пересобираем свой проект со включенной отладочной информацией.
    2. Запускаем программу под профайлером, работаем в ней некоторое время.
    3. После этого останавливаем программу и профайлер показывает какие строки нашей программы выполнялись чаще всего и откуда был вызов этих строк.

    В этом пофайлере отдельные алгоритмы сбора статистики для однопоточной программы и для многопоточной!

    Автор: imageman от июня 9, 2010

  7. Никита, ты не прав.

    Я бы не стал так утверждать :)

    Вы ничего не поняли. Ваши тесты вообще ничего не показывают. ПРосто выводится значение счетчика i. Так а при чем тут счетчик? Я говорю про машинный код, который сгенерит Delphi. Его будет больше и много лишнего.

    Ладно, не верите мне - читаем ОБЯЗАТЕЛЬНО статью “Написание оптимального кода под Delphi” на сайте DelphiMaster.ru, надеюсь, что хотя бы ему вы поверите. Кстати цитирую текст оттуда:

    Выносите инвариантный код за тело цикла. Наиболее частая ошибка – for i:=1 to length(str) do… Дело в том, что при каждой итерации будет вызываться функция length, что пагубно скажется на производительности. Рекомендуется длину строки заранее присвоить переменной. Также не включайте в тело цикла код, заведомо не зависящий от изменения итерационной переменной.
    blockquote>

    Автор: Булай Никита от июня 9, 2010

  8. :mrgreen:
    Ребята, учите матчасть, читайте официальные help’ы!

    Оптимизатор старых версий Delphi (точно не помню до какой версии, толи до Delphi 5, то ли до Delphi 6 включительно) действительно будет вычислять в цикле for выражение to в каждой итерации цикла.
    В более новых версиях Delphi, при включённой оптимизации, такого происходить не будет (спасибо разработчикам).

    И не надо слепо верить статьям, тем более в той же статье на DelphiMaster есть примечание: “Внимание! В статье имеются неточности и некорректные утверждения.”

    Автор: sw от июня 9, 2010

  9. Точно будет больше кода? Это ж легко проверить, добавим оба варианта, и посмотрим окошко 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

  10. У меня 2006. Да, эта оптимизация верна для старых версий (и все же не помешала бы и для новых). Много кто сейчас все еще юзает 6 делфи. Но кто сказал, что я ограничился только этим методом оптимизации? Читайте и еще раз читайте :)

    Автор: Булай Никита от июня 9, 2010

  11. Булай Никита, извини, что я тебя поливаю грязькой, но считаю, что это надо сделать.

    Опыта у тебя _мало_, теже оптимизации типа “заменил div 2 на shr 1″ - это, по большому счёту, забота комилятора, а не программиста.
    “поубирал лишние проверки и вообще ненужный код” - а это вот уже твоя проблема, что ты так раньше писал, и то, что ты пересматриваешь старый код, и признаёшь, что раньше писал всякую хрень - это хорошо..

    В любом случае, тебе любой Программист скажет, что оптимизация путём подстройки своего кода под компилятор - это не оптимизация (это, кстати, ещё надо в себе выработать - писать код так, чтобы потом не делать такой “оптимизаци”). В твоём случае ты получил прирост производительности 0,06%, это смешно.
    Настоящая оптимизация заключается в пересмотре алгоритмов (твоих и тех, что ты используешь).

    К примеру, недавно у меня был случай. Подпрограмма строила дерево по данным, считываемым из БД. В общем случае - до 100 строк, дерево строилось за доли секунды. Но в один прекрасный день из БД пришло 80 тысяч строк - дерево так и не построилось (пользователь ждал минут 15 - недождался).
    Оптимизацию я провёл в два этапа (с у чётом того, что SQL-запрос менять нельзя и данные из БД могут приходить в хаотичном порядке). В первый раз концептуально изменил алгоритм подпрограммы (дерево в 80 тыс. строк строилось уже где-то за минуту), в следующий раз - изменил структуру данных в подпрограмме. Теперь такое дерево строится секунд за шесть.
    Всего два с половиной часа работы и таааакой прирост производительности :razz:
    Вот тут я могу смело сказать, что “оптимизация рулит” :wink:

    Автор: sw от июня 9, 2010

  12. по большому счёту, забота комилятора, а не программиста.

    Однако он этого не делает ;)

    а это вот уже твоя проблема, что ты так раньше писал.

    А где я сказал, что это не моя проблема? Речь в заметке идет не про то, что Delphi плох в оптимизации и так далее, а о том, что код иногда надо проверять и по возможности оптимизировать. Не верно поняли смысл

    В твоём случае ты получил прирост производительности 0,06%, это смешно.

    Вот это скорее говорит о том, что “Опыта у тебя _мало_, “, а не у меня ;) Без обид. Любой прирост, даже самый несущественный, всегда может принести ту или иную пользу/выгоду. Вспомни летние олимпийские игры, когда при поднятии штанги Белорус почти стал чемпионом, но какой-то китаец, подняв столько же - выйграл! А знаешь почему? Потому что его вес был меньше на 200 грамм!!! Думаю, что идею ты понял, если нет - не надо писать об этом

    Настоящая оптимизация заключается в пересмотре алгоритмов

    Спасибо, я знаю, что такое оптимизация и в чем она заключается ;)

    Всего два с половиной часа работы и таааакой прирост производительности

    Молодец, уважаю.

    я тебя поливаю грязькой,

    А эт делать не надо :cool:

    P.S. Не этом предлагаю закончить спор о способах оптимизации, потому какк мораль сей басни такова: старайтесь писать оптимальный код :)

    Автор: Булай Никита от июня 9, 2010

  13. sw “прирост производительности 0,06%, это смешно”
    Смешнее как Вы насчитали 0.06% тогда как тут ~6%?
    Во-вторых не надо тут приводить себя в пример. Может у Вас код изначально был ужасно кривой и Вы подумав над алгоритмом добились многократного увеличения производительности, тогда как у автора код был изначально нормальным и он применив дополнительную оптимизацию добился еще 6% увеличения производительности?
    А насчет кода типа
    for i := 0 to Memo1.Lines.Count или Length(s)
    уже давно все знают что встроенный оптимизатор такое за программистами убирает.
    imageman спасибо за ссылку на этот профайлер, раньше про него ничего не знал. Юзал GProfile

    Автор: Дмитрий от июня 9, 2010

  14. 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

  15. Сегодня узнал, что если заменить:
    for i:= 0 to 200 do
    на for i:=200 downto 0 do
    то можно выиграть в оптимизации. Процессору не придется делать одной лишней операции.

    Автор: Булай Никита от Сен 1, 2010

Оставить комментарий

:mrgreen: :neutral: :twisted: :shock: :smile: :???: :cool: :grin: :oops: :razz: :roll: :wink: :cry: :lol: :sad: