![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
В поисках пикселя - benchmark [D5, Win95/98]
Mihey © (2004-03-25 17:28:00)Ради интереса производил тестирование скорости доступа к пикселям различными средствами: TCanvas.Pixels, Scanline, QPix(TQuickPixels, статья на сайте, а одним из авторов скромно выступил MBo, если не ошибаюсь) и Graphic32( http://www.g32.org, делал тоже наш - Alex Denisov). Для замерения времени был выбран компонент THPCounter- высокоточный таймер, точность до 0,000001 секунды.
Было создано 4 проекта, в которых реализовывалась одна и та же задача: просканировать изображение 800x600с глубиной цвета 24bitи присвоить значение переменной. Измерялась скорость только от начала сканирования до его завершения.
Значение в обычном случае присваивалось переменной типа TColor. Для Scanline была использована функция RGBToColorследующего вида:
function RGBToColor(R,G,B:Byte): TColor;
begin
Result:=B Shl 16 Or
G Shl 8 Or
R;
end;
Проблема вышла с Graphic32. Дело в том, что этот графический пакет определяет свой класс TBitmap32для работы с битмапами, используя при этом свой класс TColor32для хранения цвета - обычный TColor с альфа-каналом. С одной стороны, TColor32 используется в Graphic32 везде и в пределах работы с Graphic32 конвертации не требуется. С другой стороны, хотел соблюсти строгость теста, поэтому в одном из результатов использовалась функция WinColor, возвращающая TColor из TColor32.
Результаты были замерены несколько раз, на глаз выбрано что-то среднее, что ( !) не меняет картину результатов.
Тест выполнялся на компьютере PII 400 MHzс 64 Mb RAM, Win 98.
=====
TCanvas.Pixels
Этот способ показал самый медленный результат - 1.91 секунды.
=====
Scanline
Использовать этот метод довольно неудобно, но тем не менее он дал вполне приемлемый результат - 0.04 секунды.
=====
QPix
Мне он показался очень удобным. создаёшь переменную класса TQuickPixels, присоединяешься к битмапа и всё. Он показал очень хорошую скорость: 0.026 секунды! Пожалуй, если вы хотите работать в переделах класса TBitmap, то используйте этот способ.
=====
Graphic32
Использовать этот графический пакет очень просто. К обычному классу прибавляется постфикс 32 и всё. Теперь подробнее о результатах.
Присваивание переменной типа TColor32 дало результат 0.027 секунды при использовании метода TBitmap32.Pixel sи самый быстрый результат - 0.02 секунды при использовании TBitmap32.Pixel. Поясняю различие: при использовании Pixels учитывается, что координаты пикселя могут выйти из граний изображения, и тогда вернётся некоторое значение OuterColor. Очевидно, что функцию замедляет проверка.
Однако использование TBitmap32.Pixel вместе с функцией WinColor дало результат 0.04 секунды на всё изображение.
cyborg (2004-03-30 00:03:00) [21][39] Mihey © (29.03.04 21:05)
У меня игра в 16 бит и всё пучком.
dmk (2004-03-29 23:50:00) [22]>Неверно. Чем меньше bpp, тем эффективнее
Я имел в виду выбор между 24 и 32 битами.
Mihey (2004-03-29 21:05:00) [23]> Неверно. Чем меньше bpp, тем эффективнее.
Нет уж, не надо нам 16-bit. Это прошлое. Делайте движки, поддерживающие 24- или 32-bit'ные изображения.
miek (2004-03-29 20:53:00) [24]>24 бита использовать эффективнее
Неверно. Чем меньше bpp, тем эффективнее.
>Или 1262,3 кадра в секунду =)
Это ж скорость рендеринга, а не вывода. Попробуй вывести на экран, сразу поплохеет, особенно через GDI.
>Получилось, что он на 1-2% эффективнее
чем с командой REP MOVSD.
Можно и нужно быстрее. Посмотри, как у меня (хотя там тоже не предел). www.miek.narod.ru/spriteutils2.zip
Процедура TDIB.PutFast
dmk (2004-03-29 20:09:00) [25]Ах да! Совсем забыл. Вот код MoveBytes.
Тестировал на больших и долгих циклах.
Получилось, что он на 1-2% эффективнее
чем с командой REP MOVSD.
По крайней мере на p4.
procedure MoveBytes(SrcAddr, DestAddr, NumBytes: DWord);
asm
push edi
push esi
mov esi, SrcAddr
mov edi, DestAddr
mov eax, NumBytes
mov ecx, eax
sar ecx, 2
push eax
jz @@oB
@@nP:
mov eax, [esi]
mov [edi], eax
add esi, 4
add edi, 4
dec ecx
jnz @@nP
@@oB:
pop ecx
and ecx, 03h
rep movsb
pop esi
pop edi
end;
dmk (2004-03-29 20:05:00) [26]2 MegaVolt © (29.03.04 15:45) [34]
Заполнение буфера изображением
24 бита использовать эффективнее
На моем P4 2.53GHz получилось
Для 10000 заполнений ушло времени 7.922 секунды
т.е. на одно перемещение буфера 0.00079 десятитысячных секунды.
Это менее 1 миллисекунды.
Или 1262,3 кадра в секунду =)
По моему очень неплохо.
Как то вроде Miek выкладывал скоростной 2D движок.
У него тоже зашкаливало за 700 fps на моей машине =)
Только разрешение было 1024х768.
procedure TMainForm.ToolButton8Click(Sender: TObject);
var
bmp: TBitmap;
sT,eT: DWord;
BaseAddr: Pointer;
ScLen: DWord;
NumPlanes: DWord;
bW,bH: Integer;
dc: HDC;
iSize: DWord;
tmp: Pointer;
NumCycles: DWord;
begin
bmp := TBitmap.Create;
bmp.LoadFromFile('d:\done\800_600_24.bmp');
case bmp.PixelFormat of
pf24bit: NumPlanes := 3;
pf32bit: NumPlanes := 4;
else
begin
bmp.FreeImage;
bmp.Free;
Raise Exception.Create('Unknown pixel depth!');
end;
end;
bW := bmp.Width;
bH := bmp.Height;
ScLen := DWord(bW) * NumPlanes;
BaseAddr := bmp.ScanLine[bH - 1];
iSize := ScLen * DWord(bH) div NumPlanes;
GetMem(Tmp, iSize * NumPlanes);
sT := GetTickCount;
For NumCycles := 0 to 9999 do
MoveBytes(DWord(BaseAddr), DWord(Tmp), iSize);
eT := GetTickCount;
FreeMem(Tmp, iSize);
dc := GetDC(0);
BitBlt(dc, 0,0, bW, bH, bmp.Canvas.Handle, 0, 0, SRCCOPY);
ReleaseDC(0, dc);
DeleteDC(dc);
bmp.FreeImage;
bmp.Free;
ShowMessage(FloatToStrF((eT - sT) / 1000, ffNumber, 18, 5) + 'секунд затрачено' + #13 +
FloatToStrF((eT - sT) / 1000 / 10000, ffNumber, 18, 5) + ' секунд на заливку');
end;
miek (2004-03-29 19:49:00) [27]>А если нужно просто отобразить ранее подготовленный массив на экране? Возможно какая то дополнительная оптимизация?
Практически никакой, кроме пользования MMX. Тем более, отображаешь экран не ты, а GDI (или DirectX).
MegaVolt (2004-03-29 15:45:00) [28]А если нужно просто отобразить ранее подготовленный массив на экране? Возможно какая то дополнительная оптимизация?
goblin (2004-03-28 00:27:00) [29]<miek © (27.03.04 20:35) [32]
Только для точных измерений лучше, я думаю, не процедуру использовать, а прямо код раскрыть.>
Абсолютно правильно, необходимо также удостоверится, что кэш отдан нам - примитивно, но работает:
Sleep(0) перед вызовом процедур, у которых замеряется время....
Либо многократный вызов - чтобы отбросить влияние загрузки кеша данными (оценка только выполнения кода).
Может я и не прав ...но я пользуюсь этим способом более менее точно.
А для "групповой" забивки пикселей - лучше пользоваться коммандой MOVNTQ ...
miek (2004-03-27 20:35:00) [30]Только для точных измерений лучше, я думаю, не процедуру использовать, а прямо код раскрыть.
cyborg (2004-03-27 18:11:00) [31]>>[24] dmk © (27.03.04 01:26)
Используй для подсчёта:
function GetCPUTick: int64; assembler;
asm
rdtsc
end;
Точнее измерителя скорости не бывает ;).
Mihey (2004-03-27 14:29:00) [32]Последний способ для 32-bit дал такой же результат на моём компе.
miek (2004-03-27 12:06:00) [33]>По каким то причинам она работает медленнее чем Mov
>команда rep Stosd составная и крайне неэффективная
Разумеется. Быстрее она будет только на x486
А разворачивать цикл эффективно только до уровня 2 DWORD-а на итерацию, шина все равно больше не возьмет.
dmk (2004-03-27 02:39:00) [34]Решил я тоже потестировать скорость.
Загнал все в цикл. Вот так:
For NumCycles := 0 to 511 do
asm
end;
И у меня получилось, что для
32 бит скорость закраски = 1.14 сотых секунды
на 512 заливок 800х600 или 0,0022265625 секунды
на одну заливку.
Для 24 бит скорость закраски = 0.859 тысячных секунды
на 512 заливок 800х600 или 0,001677734375 секунды
на одну заливку.
Это говорит о том, что команда rep Stosd составная и крайне
не эффективная =(. Придется свой движок в некоторых местах переделывать.
PS. Завтра попробую развернуть цикл.
Может еще быстрее будет =)
dmk (2004-03-27 02:13:00) [35]А еще лучше так
asm
push edi
mov eax, c
bswap eax
shr eax, 8
mov edx, NumPlanes
mov ecx, iSize
mov edi, BaseAddr
@@nP:
mov [edi], eax
add edi, edx
dec ecx
jnz @@nP
pop edi
end;
dmk (2004-03-27 02:11:00) [36]Вся беда в коде REP STOSD.
По каким то причинам она работает медленнее чем Mov.
Замени для 32 бит код на этот. Должно быть быстрее.
asm
push edi
mov eax, c
bswap eax
shr eax, 8
mov ecx, iSize
mov edi, BaseAddr
@@nP:
mov [edi], eax
add edi, 4
dec ecx
jnz @@nP
pop edi
end;
Mihey (2004-03-27 01:34:00) [37]2 dmk:
Последний код выдал 0.006 секунд на такое изображение, и, будь моя программа вирусом, испоганила бы пользователю 150 здоровенных картинок за секунду. Я попробовал тот первый код для 32-битного изображения, но получил меньшую скорость - 0.009 секунд (сравнимо с Graphic32 и Scanline).
dmk (2004-03-27 01:26:00) [38]К сожалению у меня на P4-2.53GHz прорисовка происходит настолько быстро, что результат GetTickCount после цикла возвращается тот
же самый. Поэтому у меня время исполнения равно 0.
dmk (2004-03-27 01:23:00) [39]Этот код можно ускорить практически до скорости 32 битного.
Достаточно развернуть цикл. Только не переборщи с границами
изображения, а то AV выскочит.
dmk (2004-03-27 01:16:00) [40]вместо того ассемблерного кода вставь этот.
Но этот только для 24 бит
asm
push edi
mov eax, c
bswap eax
shr eax, 8
mov dl, al
shr eax, 8
mov ecx, iSize
mov edi, BaseAddr
@@nP:
mov [edi], dl
mov [edi + 1], ax
add edi, 3
dec ecx
jnz @@nP
pop edi
end;