Dobra, skleciłem większy kod porównujący szybkość różnych algorytmów (przy okazji proszę o ocenę zastosowanej przeze mnie metodologii).
type
TStaticMask = array[0..1079, 0..1919] of RGBTriple;
var //zmienne globalne (tak, wiem, nie powinno się, ale to tylko dla testów).
CPU, Before, After: Int64;
Bits: array of RGBTriple;
StaticMask: ^TStaticMask;
PointerMask: array of array of ^RGBTriple;
I, Index: Integer;
Output, Sum: Single;
const
Runs = 15;
{Fill Bits}
procedure FillBits;
var
I: Integer;
begin
QueryPerformanceCounter(Before);
for I := 0 to (1920 * 1080) - 1 do
begin
Bits[I].rgbtRed := 250;
Bits[I].rgbtGreen := 150;
Bits[I].rgbtBlue := 50;
end;
QueryPerformanceCounter(After);
Output := ((After - Before) * 1000) / CPU;
WriteLn('FillBits: ',(Output):0:2, ' ms');
end;
{Fill Bits Converted Func}
function Get1D(const AX, AY, AWidth: Integer): Integer; inline;
begin
Result := (AY * AWidth) + AX;
end;
procedure FillBitsConvertedFunc;
var
X, Y: Integer;
begin
QueryPerformanceCounter(Before);
for Y := 0 to 1079 do
for X := 0 to 1919 do
begin
Bits[Get1D(X, Y, 1920)].rgbtRed := 250;
Bits[Get1D(X, Y, 1920)].rgbtGreen := 150;
Bits[Get1D(X, Y, 1920)].rgbtBlue := 50;
end;
QueryPerformanceCounter(After);
Output := ((After - Before) * 1000) / CPU;
WriteLn('FillBitsConvertedFunc: ',(Output):0:2, ' ms');
end;
{Fill Bits Converted Proc}
procedure Set1D(const AX, AY, AWidth: Integer); inline;
begin
Index := (AY * AWidth) + AX;
end;
procedure FillBitsConvertedProc;
var
X, Y: Integer;
begin
QueryPerformanceCounter(Before);
for Y := 0 to 1079 do
for X := 0 to 1919 do
begin
Set1D(X, Y, 1920);
Bits[Index].rgbtRed := 250;
Bits[Index].rgbtGreen := 150;
Bits[Index].rgbtBlue := 50;
end;
QueryPerformanceCounter(After);
Output := ((After - Before) * 1000) / CPU;
WriteLn('FillBitsConvertedProc: ',(Output):0:2, ' ms');
end;
{Fill Static Mask}
procedure FillStaticMask;
var
X, Y: Integer;
begin
QueryPerformanceCounter(Before);
for Y := 0 to 1079 do
for X := 0 to 1919 do
begin
StaticMask[Y][X].rgbtRed := 250;
StaticMask[Y][X].rgbtGreen := 150;
StaticMask[Y][X].rgbtBlue := 50;
end;
QueryPerformanceCounter(After);
Output := ((After - Before) * 1000) / CPU;
WriteLn('FillStaticMask: ',(Output):0:2, ' ms');
end;
{Fill Pointer Mask}
procedure AttachPointerMask;
var
X, Y, I: Integer;
begin
I := 0;
SetLength(PointerMask, 1080);
for Y := 0 to 1079 do
begin
SetLength(PointerMask[Y], 1920);
for X := 0 to 1919 do
begin
PointerMask[Y][X] := @Bits[I];
Inc(I, 1);
end;
end;
end;
procedure FillPointerMask;
var
X, Y: Integer;
begin
QueryPerformanceCounter(Before);
for Y := 0 to 1079 do
for X := 0 to 1919 do
begin
PointerMask[Y][X].rgbtRed := 250;
PointerMask[Y][X].rgbtGreen := 150;
PointerMask[Y][X].rgbtBlue := 50;
end;
QueryPerformanceCounter(After);
Output := ((After - Before) * 1000) / CPU;
WriteLn('FillPointerMask: ',(Output):0:2, ' ms');
end;
initialization
QueryPerformanceFrequency(CPU); //pobiera częstotliwość taktowania procesora
SetLength(Bits, 1920 * 1080); //tworzy bufor z pikselami.
StaticMask := @Bits[0]; //"Nakłada" statyczną maskę na bufor.
AttachPointerMask; //Ustawia indywidualne wskaźniki na indywidualne komórki bufora
Sum := 0;
for I := 0 to Runs - 1 do
begin
FillBits;
Sum := Sum + Output;
end;
WriteLn('On average: ', (Sum / Runs):0:2);
WriteLn(' ');
Sum := 0;
for I := 0 to Runs - 1 do
begin
FillBitsConvertedFunc;
Sum := Sum + Output;
end;
WriteLn('On average: ', (Sum / Runs):0:2);
WriteLn(' ');
Sum := 0;
for I := 0 to Runs - 1 do
begin
FillBitsConvertedProc;
Sum := Sum + Output;
end;
WriteLn('On average: ', (Sum / Runs):0:2);
WriteLn(' ');
Sum := 0;
for I := 0 to Runs - 1 do
begin
FillStaticMask;
Sum := Sum + Output;
end;
WriteLn('On average: ', (Sum / Runs):0:2);
WriteLn(' ');
Sum := 0;
for I := 0 to Runs - 1 do
begin
FillPointerMask;
Sum := Sum + Output;
end;
WriteLn('On average: ', (Sum / Runs):0:2);
Wyniki:
To oczywiście tylko malutka próbka kontrolna (15 powtórzeń, żeby screen z wynikami za duży nie był ;]), ale przepuszczałem też po 50, 200 i 1000 razy, a tendencja jest zawsze ta sama (to znaczy hierarchia prędkości algorytmów się nie zmienia). No i muszę przyznać, że to co otrzymałem, jest dla mnie trochę zaskakujące. Rozumiem, że procedura pomocnicza Set1D
jest szybsza od funkcji pomocniczej Get1D
bo jednak brak kopiowania danych robi swoje, ale czemu np. modyfikacja bezpośrednio w buforze jest szybsza przy użyciu podwójnej pętli (i to jeszcze z wywołaniem procedury lub funkcji pomocniczej, co prawda z inline
, ale zawsze), od modyfikacji dokonanej w pętli jednowymiarowej? A to że maska statyczna wygrywa ze wszystkimi - jeżeli dobrze kombinuję - jest chyba związane z faktem, że Delphi (Pascal?) wolniej przetwarza tablice dynamiczne niż statyczne...? No i czemu wskaźniki na poszczególne komórki bufora są takie wolne?!
Jeżeli nie zostanie mi nic innego, to chyba zdecyduję się na konwersje współrzędnych przy pomocy procedury pomocniczej, bo to drugie, najszybsze rozwiązanie, które jednocześnie pozwoli mi bez przeszkód manipulować wymiarami bufora.