![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
статья: Написание оптимального кода под Delphi
Imp © (2005-02-02 13:12:00)
Вынесение инвариантного кода за пределы цикла – не выносится. Наиболее распространенный недочет – условие цикла записывается как:
for i:=0 to memo1.lines.count – 1 do...
Delphi будет при каждой итерации вызывать метод count, вычитать из результата 1 и потом уже сверять. Настоятельно рекомендуется переписывать подобный код как
lin := .lines.count – 1;
for i:=0 to lin do...
Весь код VCL написан с нарушением этого правила. Очевидно, что проще подобного рода оптимизацию встроить в компилятор, нежели переписывать VCL :)
Автор этой статьи, наверное, просто не смотрел ассемблерный код, который генерирует Delphi в этом случае. Вдобавок он не знаком со стандартом Object Pascal. Дело в том, что в любом случае, аргументы, которые необходимы для цикла for do, РАСЧИТЫВАЮТСЯ ТОЛЬКО ОДИН РАЗ. Это не относится к while do или repeat until. Было бы очень неплохо, если бы статьи, перед тем как попасть в руки новичков просматривались профессионалами.
GrayFace (2005-02-04 06:41:00) [1]Gloomer © (02.02.05 15:13) [12]
Достаточно этого:
for i:=0 to Memo1.Lines.Count do Memo1.Lines.Add('111');
Если бы он вычислял на каждом шаге, был бы бесконечный цикл.
Dentall (02.02.05 17:59) [21]
Еще ошибка:
Не используйте конструкции типа a:=10*sin(45*pi/180); Delphi не вычислит эту константу на этапе компиляции, напротив, будет послушно вызывать sin и pi по ходу выполнения программы! В случае, если угол является переменной, по крайней мере pi можно заменить константой 3,1415...
При включенной оптимизации pi всегда заменяется константой. 45*pi/180 - тоже. При выключенной я не проверял.
Sha (2005-02-03 23:25:00) [2]> icWasya
Задачку можешь не решать :)
Я твои 4000 почему-то прочитал как 5000.
Sha (2005-02-03 23:03:00) [3]> icWasya © (03.02.05 20:13) [29]
В твоем случае быстрее будет
for I:=0 to W*H-1 do Result:=Result+Massive[I];
Как думаешь, почему?
И еще вот такая задачка по арифметике у меня возникла
при прочтении твоего сообщения: что больше
$1000000 = 16777216или5000 * 4096 = 20480000?
icWasya (2005-02-03 20:13:00) [4]Кстати про эффективное использование памяти
Вот такая задачка возникла при обработке изображений
//TestSpeed.dpr//=============================================
program TestSpeed;
uses
Forms,
UMain in 'UMain.pas' {TestForm};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TTestForm, TestForm);
Application.Run;
end.
//UMain.dfm
object TestForm: TTestForm
Left = 309
Top = 168
Width = 264
Height = 145
Caption = 'TestForm'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 152
Top = 40
Width = 32
Height = 13
Caption = 'Label1'
end
object Label2: TLabel
Left = 152
Top = 72
Width = 32
Height = 13
Caption = 'Label2'
end
object ButtonTest1: TButton
Left = 24
Top = 32
Width = 110
Height = 25
Caption = 'Test1'
TabOrder = 0
OnClick = ButtonTest1Click
end
object ButtonTest2: TButton
Left = 24
Top = 64
Width = 110
Height = 25
Caption = 'Test2'
TabOrder = 1
OnClick = ButtonTest2Click
end
end
//=========================================
unit UMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TTestForm = class(TForm)
ButtonTest1: TButton;
ButtonTest2: TButton;
Label1: TLabel;
Label2: TLabel;
procedure FormCreate(Sender: TObject);
procedure ButtonTest1Click(Sender: TObject);
procedure ButtonTest2Click(Sender: TObject);
private
H,W:Integer;
T1,T2:TDateTime;
S:string;
Sum:Integer;
Massive : array[0..$1000000] of Byte;
Pointers : array[0..4000] of PByteArray;
procedure Init(aW:Integer);
function Test:Integer;
{ Private declarations }
public
{ Public declarations }
end;
var
TestForm: TTestForm;
implementation
{$R *.DFM}
{ TForm1 }
procedure TTestForm.Init(aW: Integer);
var
I:Integer;
begin
H:=High(Pointers);
W:=aW;
for I:=0 to H do Pointers[I]:= @Massive[I*W];
end;
procedure TTestForm.FormCreate(Sender: TObject);
var
I:Integer;
begin
Randomize;
for I:=0 to High(Massive) do Massive[I]:=Random(255);
end;
procedure TTestForm.ButtonTest1Click(Sender: TObject);
begin
Init(4096);
Sum:=Test;
Label1.Caption:=S;
end;
procedure TTestForm.ButtonTest2Click(Sender: TObject);
begin
Init(4097);
Sum:=Test;
Label2.Caption:=S;
end;
function TTestForm.Test:Integer;
var
I,J,K:Integer;
begin
T1:=Now;
Result:=0;
for J:=0 to W-1 do
for I:=0 to H-1 do
Result:=Result+Pointers[I][J];
T2:=Now;
S:=FormatFloat('### ##0.0## ms',(T2-T1)*86400*1000);
end;
end.
//===========================================================
А теперь вопрос - какой тест будет выполнен быстрее и почему??
Sha (2005-02-03 18:32:00) [5]> JK (03.02.05 16:13) [24]
>> к тому же скорость выполнения на процессорах AMD может
>> существенно упасть.
> А можно это пояснить?
AMD в руководстве по оптимизации кода для своих процессоров
не рекомендует употреблять константы в нескольких последовательно
идущих (или близких) командах. Это дейтвительно так для 3-4
подряд идущих констант.
> Alx2 © (03.02.05 17:09) [25]
> А в чем тогда смысл этого патча в оптимизаторе?
Компилятор Delphi сначала генерирует код на основе паттернов, а
затем оптимизирует получившийся код, в частности удаляет лишние
команды. В большинстве случаев он справляется с этим неплохо.
Данныйт патч, помимо чего-то, для чего он предназначен, по
ошибке разработчиков даунгрэйдит оптимизатор.
Я сам этот патч не ставил. Но по коду, который приводили другие
товарищи, похоже, что качество удаления лишнего резко ухудшается.
> miek © (03.02.05 17:45) [27]
> ... при написании компилятора было взято правило: ассемблерный
> код каждой строки не должен зависеть от кода других строк.
Это не так. Зависит. Например, если два последовательных
оператора используют общее выражение (например, адрес элемента
массива), то оно вычисляется один раз, помещается на регистр,
и потом используется повторно.
> Это нужно для того, чтобы можно было в ассемблерных вставках
> свободно без PUSH/POP использовать регистры общего назначения.
Вовсе нет. Регистры eax, edx, ecx можно свободно использовать
только потому, что в отношении их содержимого компилятор
не делает никаких предположений после ассемблерной вставки.
Если вставки не было, то компилятор в них как правило хранит
индексы и другие часто используемые данные или результаты
текущих вычислений. Другие регистры (еbx, esi, edi, ebp) надо
пушить всегда.
Примерно так написано в справке :)
miek (2005-02-03 17:45:00) [6]По поводу якобы несовершенства дельфийского оптимизатора. Его и нельзя было сделать таким же, как С++ по той простой причине, что при написании компилятора было взято правило: ассемблерный код каждой строки не должен зависеть от кода других строк. Это нужно для того, чтобы можно было в ассемблерных вставках свободно без PUSH/POP использовать регистры общего назначения. Это в справке в BASM написано. Умейте вчитываться, камрады. За все надо платить.
Cosinus (2005-02-03 17:11:00) [7]Простите за off
<offtop> Вы видели использованную литературу в конце статьи? Я понимаю, что фамилия просто похожа, но я смеялся долго, потому что автоматом вспомнился основной продукт этой компании...
Касперски К.Техника оптимизации программ. Эффективное использование памяти
</offtop>
Alx2 (2005-02-03 17:09:00) [8]> Sha © (02.02.05 18:04) [22]
А в чем тогда смысл этого патча в оптимизаторе?
JK (2005-02-03 16:13:00) [9]Sha © (02.02.05 15:24) [13]
к тому же скорость выполнения на процессорах AMD может существенно упасть.
А можно это пояснить?
ламер (2005-02-02 18:46:00) [10]Sha © (05.02.02 15:24) [13]
про case - всё правильно. нет никакой разницы, в каком порядке идут элементы, но если из них можно составить арифметическую прогрессию, то компилятор сгенерирует таблицу ссылок. если нет - будет много сравнений.
Sha (2005-02-02 18:04:00) [11]Dentall (02.02.05 17:59) [21]
Снеси нафиг обновление 7.01.
Компилятор после него генерит медленный код.
Dentall (2005-02-02 17:59:00) [12]Еще хочу заметить - в новой версии статьи также будет указано "не выносится инвариантный код". Например, если в теле цикла сделать "левое" присвоение, оно будет присваиваться каждую итерацию. Неверное "руководство к действию" удалено.
Владислав (2005-02-02 17:51:00) [13]> Sha © (02.02.05 17:27) [17]
Мдя... вообще-то я только в шестерке и проверял :о)
Так что хорошо, что версии одинаковые :о)
Dentall (2005-02-02 17:47:00) [14]Согласен, допустил грубейшую ошибку. (про циклы). Виной тому - моя преступная невнимательность. (не включил оптимизатор). Что касается изменений, то они высланы, и в скором времени будет все исправлено. Тесты проводились на половина на 7.0, половина уже после обновления до 7.01. Еще раз приношу извинеия.
pasha_golub (2005-02-02 17:45:00) [15]Вот и свершилось. Наконец-то статьи начали обсуждать в форуме. Считаю это, хорошим делом. Есть предложение для каждой новой статьи создавать ветку. Как вам идея?
Sha (2005-02-02 17:27:00) [16]Владислав © (02.02.05 16:33) [16]
Теперь и я проверил. Компилятор поумнел.
Похоже, D6 строит таблицы и деревья переходов при первой возможности.
Интересно, начиная с какой версии?
Сам case почти не использую.
На мой взгляд важнее было бы циклы оптимизировать.
Владислав (2005-02-02 16:33:00) [17]Уже проверял. В свое время у меня на эту тему спор (или, скорее, обсуждение) был с Игорем Шевченко. Он сказал то же самое, и я проверил ;)
Sha (2005-02-02 16:28:00) [18]Владислав © (02.02.05 16:19) [14]
а ты проверь ;)
Владислав (2005-02-02 16:19:00) [19]Sha © (02.02.05 15:24) [13]
> Неточность: здесь имеется ввиду возрастающая последовательность.
Только пользы от этого все равно никакой :)
Sha (2005-02-02 15:24:00) [20]2 Poirot © (02.02.05 14:58) [11]
http://www.delphimaster.ru/articles/optimization.html
Еще что замечено:
> В case of при возможности используйте элементы, расположенные
> в арифметической прогрессии
Неточность: здесь имеется ввиду возрастающая последовательность.
> Не используйте переменные для временного хранения констант ...
по обстоятельствам: иногда полезно, иногда вредно
> или обязательно объявляйте «магические» числа как const,
еще лучше магические числа вычислять на этапе компиляции,
о чем, впрочем, автор упоминает ниже
> либо подставляйте в код непосредственные значения
и получите код, который будет невозможно сопровождать,
к тому же скорость выполнения на процессорах AMD может существенно упасть.