Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.02.20;
Скачать: [xml.tar.bz2];

Вниз

статья: Написание оптимального кода под Delphi   Найти похожие ветки 

 
Imp   (2005-02-02 13:12) [0]


 Вынесение инвариантного кода за пределы цикла – не выносится. Наиболее распространенный недочет – условие цикла записывается как:

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. Было бы очень неплохо, если бы статьи, перед тем как попасть в руки новичков просматривались профессионалами.


 
TUser ©   (2005-02-02 13:35) [1]


> Было бы очень неплохо, если бы статьи, перед тем как попасть
> в руки новичков просматривались профессионалами.

Согласен. Осталось найти профессионала, который готов добровольно читать статьи для новичков.


 
КаПиБаРа ©   (2005-02-02 13:40) [2]

Imp   (02.02.05 13:12)
Достаточно иметь возможность обсуждения статьи на том ресурсе, где статья опубликована. А уж автор прочитав комментарии исправит ошибки в своем творении. Ему же не хочется что бы на него пальцем показывали. И не надо никаких профессионалов напрягать.


 
Poirot ©   (2005-02-02 14:29) [3]

Ну мб он просто вспомнил си:)))


 
Virgo_Style ©   (2005-02-02 14:35) [4]

Вероятно, человек просто привык оптимизировать программы, "не дожидаясь милостей" от компилятора... А вы сразу ругаться :)
Imho, именно это и есть профессионализм... Хотя возможности компилятора тоже надо знать :)


 
REA   (2005-02-02 14:39) [5]

Мне почему-то кажется, что функция будет вызываться чаще. И если в цикле memo1.lines.count поменяется, то результат правильный. Ассемблерный код смотрел давно. Поправьте, если это не так.


 
Sha ©   (2005-02-02 14:40) [6]

Еще ошибки:

> Здесь учитывается особенность самой операции div – округление
> в большую сторону.

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

Есть спорные моменты:

> b:=random(maxint); // b – заведомо не константа !
> a:=$abcd6123;
> if b>a then b:=a;
> Скомпилированный код будет выглядеть
> mov eax, $7fffffff // MaxInt
> call @RandInt
> mov ebx,eax
> mov eax, $abcd6123
> cmp eax, ebx
> jnl +$02
> А ведь значение, присвоенной переменной «а» являлось
> константой и наш пример можно было бы переписать как:
> b:=random(maxint);
> a:=$abcd6123;
> if b>$abcd6123 then b:= $abcd6123;


 
Neznaika ©   (2005-02-02 14:43) [7]

Не хочу спорить просто интересно, (ни когда об этом не задумывался, да и вообще больше люблю repeat)!

Предположим что в цикле предложенном автором ветки, memo.lines.count=5
а в процессе цикла туда прибавляются еще строки, так что цикл новые строки будет игнорировать? ,и остановится там где ему сказали первый раз?


 
MBo ©   (2005-02-02 14:46) [8]

>Neznaika ©   (02.02.05 14:43) [7]
В заглавном посте уже сказано, что по стандарту языка for задает количество итераций один раз.


 
Sha ©   (2005-02-02 14:49) [9]

Neznaika ©   (02.02.05 14:43) [7]
да


 
Neznaika ©   (2005-02-02 14:51) [10]

Тогда не зря я больше люблю repeat!!!   :)


 
Poirot ©   (2005-02-02 14:58) [11]

А мона на это чудо ссылочку дать?!:))


 
Gloomer ©   (2005-02-02 15:13) [12]

>Neznaika ©   (02.02.05 14:43) [7]
ответ на твой вопрос


procedure TForm1.Button1Click(Sender: TObject);
var i,j:integer;
begin
Memo1.Clear;
for i:=1 to 5 do Memo1.Lines.Add("");
j:=0;
for i:=0 to Memo1.Lines.Count-1 do
 begin
Memo1.Lines.Add("111");
inc(j);
 end;
ShowMessage(IntToStr(j));
end;


 
Sha ©   (2005-02-02 15:24) [13]

2 Poirot ©   (02.02.05 14:58) [11]
http://www.delphimaster.ru/articles/optimization.html

Еще что замечено:

> В case of при возможности используйте элементы, расположенные
> в арифметической прогрессии

Неточность: здесь имеется ввиду возрастающая последовательность.

> Не используйте переменные для временного хранения констант ...

по обстоятельствам: иногда полезно, иногда вредно

> или обязательно объявляйте «магические» числа как const,

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

> либо подставляйте в код непосредственные значения

и получите код, который будет невозможно сопровождать,
к тому же скорость выполнения на процессорах AMD может существенно упасть.


 
Владислав ©   (2005-02-02 16:19) [14]

Sha ©   (02.02.05 15:24) [13]
> Неточность: здесь имеется ввиду возрастающая последовательность.


Только пользы от этого все равно никакой :)


 
Sha ©   (2005-02-02 16:28) [15]

Владислав ©   (02.02.05 16:19) [14]
а ты проверь ;)


 
Владислав ©   (2005-02-02 16:33) [16]

Уже проверял. В свое время у меня на эту тему спор (или, скорее, обсуждение) был с Игорем Шевченко. Он сказал то же самое, и я проверил ;)


 
Sha ©   (2005-02-02 17:27) [17]

Владислав ©   (02.02.05 16:33) [16]

Теперь и я проверил. Компилятор поумнел.
Похоже, D6 строит таблицы и деревья переходов при первой возможности.
Интересно, начиная с какой версии?

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


 
pasha_golub ©   (2005-02-02 17:45) [18]

Вот и свершилось. Наконец-то статьи начали обсуждать в форуме. Считаю это, хорошим делом. Есть предложение для каждой новой статьи создавать ветку. Как вам идея?


 
Dentall   (2005-02-02 17:47) [19]

Согласен, допустил грубейшую ошибку. (про циклы). Виной тому - моя преступная невнимательность. (не включил оптимизатор). Что касается изменений, то они высланы, и в скором времени будет все исправлено. Тесты проводились на половина на 7.0, половина уже после обновления до 7.01. Еще раз приношу извинеия.


 
Владислав ©   (2005-02-02 17:51) [20]

> Sha ©   (02.02.05 17:27) [17]

Мдя... вообще-то я только в шестерке и проверял :о)
Так что хорошо, что версии одинаковые :о)


 
Dentall   (2005-02-02 17:59) [21]

Еще хочу заметить - в новой версии статьи также будет указано "не выносится инвариантный код". Например, если в теле цикла сделать "левое" присвоение, оно будет присваиваться каждую итерацию. Неверное "руководство к действию" удалено.


 
Sha ©   (2005-02-02 18:04) [22]

Dentall   (02.02.05 17:59) [21]

Снеси нафиг обновление 7.01.
Компилятор после него генерит медленный код.


 
ламер ©   (2005-02-02 18:46) [23]

Sha ©   (05.02.02 15:24) [13]
про case - всё правильно. нет никакой разницы, в каком порядке идут элементы, но если из них можно составить арифметическую прогрессию, то компилятор сгенерирует таблицу ссылок. если нет - будет много сравнений.


 
JK   (2005-02-03 16:13) [24]

Sha ©   (02.02.05 15:24) [13]
к тому же скорость выполнения на процессорах AMD может существенно упасть.

А можно это пояснить?


 
Alx2 ©   (2005-02-03 17:09) [25]

>Sha ©   (02.02.05 18:04) [22]

А в чем тогда смысл этого патча в оптимизаторе?


 
Cosinus ©   (2005-02-03 17:11) [26]

Простите за off
<offtop> Вы видели использованную литературу в конце статьи? Я понимаю, что фамилия просто похожа, но я смеялся долго, потому что автоматом вспомнился основной продукт этой компании...
Касперски К. Техника оптимизации программ. Эффективное использование памяти
</offtop>


 
miek ©   (2005-02-03 17:45) [27]

По поводу якобы несовершенства дельфийского оптимизатора. Его и нельзя было сделать таким же, как С++ по той простой причине, что при написании компилятора было взято правило: ассемблерный код каждой строки не должен зависеть от кода других строк. Это нужно для того, чтобы можно было в ассемблерных вставках свободно без PUSH/POP использовать регистры общего назначения. Это в справке в BASM написано. Умейте вчитываться, камрады. За все надо платить.


 
Sha ©   (2005-02-03 18:32) [28]

> 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) надо
пушить всегда.
Примерно так написано в справке :)


 
icWasya ©   (2005-02-03 20:13) [29]

Кстати про эффективное использование памяти
Вот такая задачка возникла при обработке изображений

//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 23:03) [30]

> icWasya ©   (03.02.05 20:13) [29]

В твоем случае быстрее будет

for I:=0 to W*H-1 do Result:=Result+Massive[I];

Как думаешь, почему?

И еще вот такая задачка по арифметике у меня возникла
при прочтении твоего сообщения: что больше

$1000000 = 16777216    или    5000 * 4096 = 20480000 ?


 
Sha ©   (2005-02-03 23:25) [31]

> icWasya
Задачку можешь не решать :)
Я твои 4000 почему-то прочитал как 5000.


 
GrayFace ©   (2005-02-04 06:41) [32]

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 - тоже. При выключенной я не проверял.



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2005.02.20;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.55 MB
Время: 0.067 c
3-1106311445
Бульбаш
2005-01-21 15:44
2005.02.20
При использованиии кэширования очищается ли кэш


4-1104265119
Pavia
2004-12-28 23:18
2005.02.20
Как скопировать изоброжение чужого окна.


3-1105949104
KAA
2005-01-17 11:05
2005.02.20
MSSQL 2000 Настройка прав для связанных серверов


1-1107341925
TP
2005-02-02 13:58
2005.02.20
Virtual ListView


9-1096658385
dimodim
2004-10-01 23:19
2005.02.20
Ну Вот пищу игру зацените





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский