Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.06.03;
Скачать: CL | DM;

Вниз

Можно ли как то оптимизировать данный участок кода   Найти похожие ветки 

 
DVM ©   (2007-05-08 14:42) [0]


type
 
 PFColor =^TFColor;
 TFColor = packed record
   b,g,r: Byte;
 end;

 TMatrixItem = record
   Blue: Cardinal;
   Green: Cardinal;
   Red: Cardinal;
   Sum: Cardinal;
 end;
 PMatrixItem = ^TMatrixItem;

var
 pmi: PMatrixItem;
 pc: PFColor;

//------------------------

 inc(pmi^.blue, pc^.b);
 inc(pmi^.green, pc^.g);
 inc(pmi^.red, pc^.r);
 inc(pmi^.sum);
 Inc(pc);

//-------------------------

Отчеркнутый фрагмент выполняется сотни тысяч раз. Можно ли как ускорить эти операции?


 
Ega23 ©   (2007-05-08 14:52) [1]

На асме его написать?


 
DVM ©   (2007-05-08 14:53) [2]


> На асме его написать?

Не владею в такой степени, чтобы обставить компилятор Delphi в плане скорости.


 
X9 ©   (2007-05-08 14:55) [3]

Большой разницы между данным фрагментом (при использовании оптимизации) и его ASM-вариантом быть не должно.


 
DVM ©   (2007-05-08 14:56) [4]

Тут прямо хочется как-то одним действием увеличить все три переменных blue, green, red :)


 
MBo ©   (2007-05-08 14:59) [5]

>одним действием увеличить все три переменных blue, green, red :)
для этого MMX можно использовать, но накладные расходы на подготовку данных будут значительны, так что выигрыша может и не быть


 
X9 ©   (2007-05-08 14:59) [6]

Одним действие не получится, ибо 3 cardinal в один 32-битный регистр не поместятся. А почему именно Cardinal? Если это RGB, то Byte хватит за глаза. А если Byte, то уже есть возможность немного схитрить.


 
DVM ©   (2007-05-08 15:02) [7]


> А почему именно Cardinal?

Вычисляется средний цвет прямоугольного фрагмента изображения.

Cardinal - это суммы значений точек в клетке по каналам.

Так сделано для ускорения работы - после суммирования всех точек в клетке вычисляется средний цвет один раз.


 
MBo ©   (2007-05-08 15:31) [8]

var
ca: array of TFColor;
mi: TMatrixItem;
pc: PFColor;
i, N: Integer;
t: DWord;
begin
N := 1024*1024*64;
SetLength(ca, N);
FillChar(mi, SizeOf(mi), 0);
pc := @ca[0];
t := GetTickCount;
for i := 0 to N - 1 do begin
  inc(mi.blue, pc^.b);
  inc(mi.green, pc^.g);
  inc(mi.red, pc^.r);
  inc(mi.sum);
  Inc(pc);
end;
Caption := IntToStr(mi.Sum + mi.Blue +mi.Green +mi.Red) +" " + IntToStr(GetTickCount - t);
end;


Хм... Этот код для 64 мегапикселов (192 мегабайта) выполняется у меня за 453 мс, т.е. скорость обработки 150 мегапикселов в секунду. Мало? ;)


 
Rouse_ ©   (2007-05-08 15:34) [9]

type
PFColor =^TFColor;
TFColor = packed record
  b,g,r: Byte;
end;

TMatrixItem = record
  Blue: Cardinal;
  Green: Cardinal;
  Red: Cardinal;
  Sum: Cardinal;
end;
PMatrixItem = ^TMatrixItem;

procedure ModifyMatrix(MatrixItems: PMatrixItem; Color: PFColor;
 const MatrixItemsCount: Integer); assembler;
asm
 push eax
 push ebx
 push ecx
 push edx
 push edi
 push esi

 mov  esi, eax // MatrixItems
 mov  bx, [edx]
 mov  dl, [edx + 2] // Color

@loop:
 mov eax, [esi]
 add al, bl
 mov [esi], eax
 mov eax, [esi + 4]
 add al, bh
 mov [esi + 4], eax
 mov eax, [esi + 8]
 add al, dl
 mov [esi + 8], eax
 inc [esi + 12]
 add esi, 16
 loop @loop

 pop  esi
 pop  edi
 pop  edx
 pop  ecx
 pop  ebx
 pop  eax
end;

procedure TForm5.Button1Click(Sender: TObject);
var
 pmi, pmiTmp: PMatrixItem;
 pc: TFColor;
 I: Integer;
begin
 GetMem(pmi, SizeOf(TMatrixItem) * 4);
 try
   pc.b := 10;
   pc.g := 20;
   pc.r := 30;
   pmiTmp := pmi;
   for I := 1 to 4 do
   begin
     pmiTmp^.Blue := I * 10;
     pmiTmp^.Green := I * 11;
     pmiTmp^.Red := I * 12;
     pmiTmp^.Sum := I * 13;

     Memo1.Lines.Add(Format("%d.%d.%d.%d",
       [pmiTmp^.Blue, pmiTmp^.Green, pmiTmp^.Red, pmiTmp^.Sum]));

     Inc(pmiTmp);
   end;
   ModifyMatrix(pmi, @pc, 4);
   pmiTmp := pmi;
   for I := 0 to 3 do
   begin
     Memo1.Lines.Add(Format("%d.%d.%d.%d",
       [pmiTmp^.Blue, pmiTmp^.Green, pmiTmp^.Red, pmiTmp^.Sum]));
     Inc(pmiTmp);
   end;
 finally
   FreeMem(pmi);
 end;
end;


 
DVM ©   (2007-05-08 15:35) [10]


> Мало? ;)

Я детектор движения пишу. Сейчас скорость 500 fps при размере кадра 640*480 на одном ядре. Цель -1000 FPS. Но видимо в этом участе кода уже предел - буду другие места мучить. :)


 
Rouse_ ©   (2007-05-08 15:38) [11]

Не вот так проще:

procedure ModifyMatrix(MatrixItems: PMatrixItem; Color: PFColor;
 const MatrixItemsCount: Integer); assembler;
asm
 push eax
 push ebx
 push ecx
 push edx

 mov  bx, [edx]
 mov  dl, [edx + 2]

@loop:
 add [eax], bl
 add [eax + 4], bh
 add [eax + 8], dl
 inc [eax + 12]
 add eax, 16
 loop @loop

 pop  edx
 pop  ecx
 pop  ebx
 pop  eax
end;


 
DVM ©   (2007-05-08 15:52) [12]


> Rouse_ ©   (08.05.07 15:38) [11]

У меня цикл с 1 а не с нуля


 
Rouse_ ©   (2007-05-08 15:53) [13]

А этому коду пофигу... Он знает размер буффера и начальный адрес буффера. Хоть с десятки начинай


 
DVM ©   (2007-05-08 16:00) [14]


> Rouse_ ©   (08.05.07 15:53) [13]

Тогда че-то не так

Было и работает:


for cy := 1 to ty do
     begin
       for cx := 1 to tx do
       begin
         inc(pmi^.blue, pc^.b);
         inc(pmi^.green, pc^.g);
         inc(pmi^.red, pc^.r);
         Inc(pc);
       end;
       inc(integer(pc), Delta);
     end;
     pmi^.sum := ty * tx;


Стало, но не работает:


  for cy := 1 to ty do
    begin
        ModifyMatrix(pmi, pc, tx);
        inc(integer(pc), Delta);
     end;
   pmi^.sum := ty * tx;


 
DVM ©   (2007-05-08 16:10) [15]


> > Rouse_ ©

У тебя в цикле на ASM вроде указатель pc не сдвигается.


 
Rouse_ ©   (2007-05-08 16:11) [16]

Пропустил что второй параметр у тебя тоже массив :)
Тогда:

procedure ModifyMatrix(MatrixItems: PMatrixItem; Color: PFColor;
 const MatrixItemsCount: Integer); assembler;
asm
 push eax
 push ebx
 push ecx
 push edx

@loop:
 mov  bx, [edx]
 add [eax], bl
 add [eax + 4], bh
 mov bl, [edx + 2]
 add [eax + 8], bl
 inc [eax + 12]
 add eax, 16
 add edx, 3
 loop @loop

 pop  edx
 pop  ecx
 pop  ebx
 pop  eax
end;


 
DVM ©   (2007-05-08 16:11) [17]

Не я неправ, все на месте


 
Rouse_ ©   (2007-05-08 16:13) [18]

Эээ, погоди. Вот это не то что было в начале:

for cy := 1 to ty do
    begin
      for cx := 1 to tx do
      begin
        inc(pmi^.blue, pc^.b);
        inc(pmi^.green, pc^.g);
        inc(pmi^.red, pc^.r);
        Inc(pc);
      end;
      inc(integer(pc), Delta);
    end;
    pmi^.sum := ty * tx;


 
Rouse_ ©   (2007-05-08 16:15) [19]

короче на пальцах...
Мой код принимает на вход два массива PMatrixItem и PFColor, каждый цветовой элемент первого массива увеличивается на значение цвета из второго массива а pmi^.sum просто увеличивается на единицу... Оно?


 
DVM ©   (2007-05-08 16:16) [20]


> Эээ, погоди. Вот это не то что было в начале:

Да, я знаю, я поправил твой код там одна строка лишняя получается, но все равно не фурычит че-то.

Я так понял твоя функция это замена внутреннего цикла?


 
Rouse_ ©   (2007-05-08 16:17) [21]

угу


 
DVM ©   (2007-05-08 16:19) [22]


> Оно?

Оно вроде. pmi^.sum  можно выкинуть из цикла оно и так рассчитывается.


 
DVM ©   (2007-05-08 16:21) [23]


> угу

Или я че-то непонимаю, но в результате замены все работает не так как ранее.


 
Rouse_ ©   (2007-05-08 16:22) [24]

Ну я как твою задачу понял - так ее и реализовал :)
Либо неверно я понял, либо ты ее неверно описал :)


 
DVM ©   (2007-05-08 16:24) [25]

Короче вот:

Было:


     for cy := 1 to ty do
     begin
      //ModifyMatrix(pmi, pc, tx);
       for cx := 1 to tx do
       begin
         inc(pmi^.blue, pc^.b);
         inc(pmi^.green, pc^.g);
         inc(pmi^.red, pc^.r);
         pmi^.sum := ty * tx;
         Inc(pc);
       end;
       inc(integer(pc), Delta);
     end;
     


Меняем:


     for cy := 1 to ty do
     begin
       ModifyMatrix(pmi, pc, tx);
       //for cx := 1 to tx do
       //begin
         //inc(pmi^.blue, pc^.b);
         //inc(pmi^.green, pc^.g);
         //inc(pmi^.red, pc^.r);
         //pmi^.sum := ty * tx;
         //Inc(pc);
       //end;
       inc(integer(pc), Delta);
     end;
     


Где:

procedure ModifyMatrix(MatrixItems: PMatrixItem; Color: PFColor;
const MatrixItemsCount: Integer); assembler;
asm
push eax
push ebx
push ecx
push edx

@loop:
mov  bx, [edx]
add [eax], bl
add [eax + 4], bh
mov bl, [edx + 2]
add [eax + 8], bl
inc [eax + 12]
add eax, 16
add edx, 3
loop @loop

pop  edx
pop  ecx
pop  ebx
pop  eax
end;


Не работает.


 
DVM ©   (2007-05-08 16:25) [26]

pmi^.sum := ty * tx;

только надо убрать в первом коде


 
Rouse_ ©   (2007-05-08 16:26) [27]

А, ну тогда естественно не так...
Сек...


 
Rouse_ ©   (2007-05-08 16:29) [28]

Вот это закоментрарь:

 //inc [eax + 12]
 //add eax, 16


 
DVM ©   (2007-05-08 16:33) [29]

Один фиг, смотри:

Постараюсь без неточностей:

Было:

     for cy := 1 to ty do
     begin
       //ModifyMatrix(pmi, pc, tx);
       for cx := 1 to tx do
       begin
         inc(pmi^.blue, pc^.b);
         inc(pmi^.green, pc^.g);
         inc(pmi^.red, pc^.r);
         Inc(pc);
       end;
       inc(integer(pc), Delta);
     end;
     pmi^.sum := ty * tx;

Стало:



    for cy := 1 to ty do
     begin
       ModifyMatrix(pmi, pc, tx);
       inc(integer(pc), Delta);
     end;
     pmi^.sum := ty * tx;



Где:


procedure ModifyMatrix(MatrixItems: PMatrixItem; Color: PFColor;
const MatrixItemsCount: Integer); assembler;
asm
push eax
push ebx
push ecx
push edx

@loop:
mov  bx, [edx]
add [eax], bl
add [eax + 4], bh
mov bl, [edx + 2]
add [eax + 8], bl
//inc [eax + 12]
//add eax, 16
add edx, 3
loop @loop

pop  edx
pop  ecx
pop  ebx
pop  eax
end;


Это не одно и то же выходит.


 
Rouse_ ©   (2007-05-08 16:37) [30]

Ты лучше результаты первого и второго покажи... Что у тебя на входе находится в pmi^ и на выходе в момент выполнения inc(integer(pc), Delta);


 
DVM ©   (2007-05-08 16:47) [31]


> Rouse_ ©

А какая строка в твоем коде отвечает за количество оборотов цикла?


 
Rouse_ ©   (2007-05-08 16:49) [32]

const MatrixItemsCount: Integer
Указывает на размер буффера во втором параметре. т.е. кол-во элементов типа ТFColor


 
DVM ©   (2007-05-08 16:54) [33]


> Rouse_ ©   (08.05.07 16:49) [32]

Теперь мне все понятно, подумаю сам дальше. Твоя функция на 10% быстрее вроде как чем мой код на паскале, но почему то дает ложные срабатывания детектора, т.е. в pmi^ где то заносятся левые значения.

Почему - неясно пока.

Результаты тут привести проблематично - я смотрю уже на последствия изменений - когда сравниваются два кадра изображения, сами же значения в pmi^ мне мало о чем скажут. На входе там нули.


 
Rouse_ ©   (2007-05-08 16:56) [34]


> в pmi^ где то заносятся левые значения.

Вероятно размер буффера более меньший, чем указан в третьем параметре...


 
Sapersky   (2007-05-08 17:21) [35]

procedure GetSumA(Src : PFColor; Dst : PMatrixItem; Count : Integer);
asm
 push ebx
 push esi
 push edi
 push edx
 mov esi, eax   // Src -> esi
 mov eax, edx
 mov ebx, [eax] // unpacking Dst to ebx, edx, edi
 mov edx, [eax + 4]
 mov edi, [eax + 8]
@loop:
 movzx eax, byte ptr [esi]
 add ebx, eax // b
 movzx eax, byte ptr [esi + $01]
 add edx, eax // g
 movzx eax, byte ptr [esi + $02]
 add edi, eax // r
 add esi, 3
 dec ecx
jnz @loop
 mov eax, edx
 pop edx
 mov [edx], ebx
 mov [edx + 4], eax
 mov [edx + 8], edi
 pop edi
 pop esi
 pop ebx
end;

pmi.blue, pmi.green, pmi.red хранятся в регистрах, за счёт чего получается существенно быстрее.
Копирайт, скажу честно, принадлежит дельфийскому оптимизатору. Вот это даёт практически аналогичный результат:

procedure GetSum(Src : PFColor; Dst : PMatrixItem; Count : Integer);
Var n, r, g, b : Integer;
begin
b := Dst.Blue; g := Dst.Green; r := Dst.Red;
for n := 0 to Count - 1 do begin
 inc(r, Src.b);
 inc(g, Src.g);
 inc(b, Src.r);
 Inc(Src);
end;
Dst.Blue := b; Dst.Green := g; Dst.Red := r;
end;

но присвоение начальных значений r,g,b занимает один из регистров, и получается немного медленнее. Если присваивать 0 - скорость такая же, как и у asm.


 
DVM ©   (2007-05-08 17:36) [36]


> Sapersky   (08.05.07 17:21) [35]

Вот с твоим вариантом проблем нет - работает нормально.

мой вариант - 450 fps
GetSum 470
GetSumА 550

Вот только непонятен мне посл вариант немного.

PS. Хочу сделать (точнее сделал) детектор движения, как расширение FastDIB.


 
Sapersky   (2007-05-08 19:35) [37]

Вот только непонятен мне посл вариант немного.
Это практически копия CPU window от GetSum.
movzx eax, byte ptr [esi]
это то же самое что и
xor eax, eax
mov al, [esi]
но быстрее, несмотря даже на то, что xor можно вынести из цикла.

Можно вообще избавиться от asm, если использовать GetSum вот в таком варианте:
procedure GetSum(Src : PFColor; Dst : PMatrixItem; Count : Integer);
Var n, r, g, b : Integer;
begin
b := 0; g := 0; r := 0;
for n := 0 to Count - 1 do begin
inc(r, Src.b);
inc(g, Src.g);
inc(b, Src.r);
Inc(Src);
end;
Dst.Blue := b; Dst.Green := g; Dst.Red := r;
end;

Var mi : TMatrixItem;

for cy := 1 to ty do begin
 GetSum(pc, @mi, tx);
 Inc(pmi.blue, mi.blue);
 Inc(pmi.green, mi.green)
 Inc(pmi.red, mi.red);
<...>

скорость должна быть такая же, что и с GetSumA.
Так что ассемблер знать надо, но писать на нём вовсе не обязательно (за исключением MMX/SSE, но это штука весьма специфичная и далеко не всегда применимая).

PS. Хочу сделать (точнее сделал) детектор движения, как расширение FastDIB.

В смысле выложить код? Это можно посмотреть, глядишь, ещё чего оптимизируем.


 
DVM ©   (2007-05-09 13:39) [38]


> Sapersky   (08.05.07 17:21) [35]

А ведь все таки и твоя функция работает не совсем правильно, я сразу и не заметил.

Правильно работает, только если писать так:


procedure GetSum(var Src : PFColor; var Dst : PMatrixItem; Count : Integer);
 Var n, r, g, b : Integer;
begin
 b := Dst.Blue; g := Dst.Green; r := Dst.Red;
 for n := 0 to Count - 1 do
   begin
     inc(r, Src.b);
     inc(g, Src.g);
     inc(b, Src.r);
     Inc(Src);
   end;
 Dst.Blue := b; Dst.Green := g; Dst.Red := r;
end;


но с var медленно очень, в два раза медленнее.


 
DVM ©   (2007-05-09 13:44) [39]


> В смысле выложить код? Это можно посмотреть, глядишь, ещё
> чего оптимизируем.

Да, но сначала его причесать надо, у меня там пока много спорных моментов в плане и скорости и функциональности. Детектор должен быть очень быстрым и несложным - в духе FastDIB. Собственно почти уже готово.


 
DVM ©   (2007-05-09 14:36) [40]

А вот так все работает очень хорошо. Но пришлось отказаться от доп процедуры, ибо доп процедура работает правильно только при указании ключевого слова var, но это медленно.

Итак, было (510 fps):

for cy := 1 to ty do
 begin
   for cx := 1 to tx do
     begin
       inc(pmi^.blue, pc^.b);
       inc(pmi^.green, pc^.g);
       inc(pmi^.red, pc^.r);
       Inc(pc);
     end;
  inc(integer(pc), Delta);
end;


Стало (660 fps):


for cy := 1 to ty do
 begin
   b := 0; g := 0; r := 0;
   for CX := 1 to TX do
     begin
       inc(B, pc^.b);
       inc(g, pc^.g);
       inc(R, pc^.r);
       Inc(PC);
     end;
   mi.Blue := b; mi.Green := g; mi.Red := r;
   Inc(pmi.blue, mi.blue);
   Inc(pmi.green, mi.green);
   Inc(pmi.red, mi.red);
   inc(integer(pc), Delta);
 end;


Код стал странноват, но быстрее на 20 процентов.

Интересно, а еще быстрее можно?



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

Текущий архив: 2007.06.03;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.044 c
3-1173705345
Thely
2007-03-12 16:15
2007.06.03
Интеграция FIBPlus и FastReport 3.0?


15-1178298367
N3xt
2007-05-04 21:06
2007.06.03
задачка)


3-1173871237
Sour Smile
2007-03-14 14:20
2007.06.03
Обновление автоинкремента в мёртвых запросах


6-1164258046
NovaC
2006-11-23 08:00
2007.06.03
Аналог ClrScr &amp; KeyPressed в Delphi


2-1179132870
Taniana
2007-05-14 12:54
2007.06.03
Подскажите путь в программе с IP-адресом!!!