![]() |
| ||
|
|
Новости |
Поиск |
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 секунды на всё изображение.
dmk (2004-03-27 01:08:00) [41]Скорее всего у тебя формат картинки 24 бита, а я написал для
32 бит. Сейчас для 24 напишу.
Mihey (2004-03-27 00:53:00) [42]Выдаёт AV на строке:
rep StosD
Использую D5. Может из-за этого?
dmk (2004-03-27 00:26:00) [43]Быстрее последнего нет ничего, если только
не какая-нибудь супер пупер оптимизация =)
dmk (2004-03-27 00:24:00) [44]Попробуй так:
type
PixelFunc = Procedure(Addr,Value:DWord);
Procedure SetP32(Addr,Value:DWord);
asm
bswap edx
shr edx, 8
mov [eax], edx
end;
Procedure SetP24(Addr,Value:DWord);
asm
bswap edx
shr edx, 8
mov [eax], dl
shr edx, 8
mov [eax + 1], dx
end;
procedure TMainForm.ToolButton8Click(Sender: TObject);
var
bmp: TBitmap;
sT,eT: DWord;
x,y: Integer;
c: TColor;
BaseAddr: DWord;
RowAddr: DWord;
ScLen: DWord;
NumPlanes: DWord;
PFunc: PixelFunc;
bW,bH: Integer;
dc: HDC;
begin
bmp := TBitmap.Create;
bmp.LoadFromFile('d:\done\800_600.bmp');
case bmp.PixelFormat of
pf24bit:
begin
NumPlanes := 3;
PFunc := SetP24;
end;
pf32bit:
begin
NumPlanes := 4;
PFunc := SetP32;
end;
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 := DWord(bmp.ScanLine[0]);
c := clYellow;
sT := GetTickCount;
For y := 0 to bH - 1 do
begin
RowAddr := BaseAddr;
For x := 0 to bW - 1 do
begin
PFunc(RowAddr, C);
Inc(RowAddr, NumPlanes);
end;
Dec(BaseAddr, ScLen);
end;
eT := GetTickCount;
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, 18) + ' секунд затрачено');
end;
Ну а если надо очистить, то это еще проще =)
procedure TMainForm.ToolButton8Click(Sender: TObject);
var
bmp: TBitmap;
sT,eT: DWord;
c: ColorRef;
BaseAddr: Pointer;
ScLen: DWord;
NumPlanes: DWord;
bW,bH: Integer;
dc: HDC;
iSize: DWord;
begin
bmp := TBitmap.Create;
bmp.LoadFromFile('d:\done\800_600.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;
c := clYellow;
sT := GetTickCount;
asm
push edi
mov eax, c
bswap eax
shr eax, 8
mov ecx, iSize
mov edi, BaseAddr
rep StosD
pop edi
end;
eT := GetTickCount;
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, 18) + ' ñåêóíä ïîòðà÷åíî');
end;
Mihey (2004-03-26 23:40:00) [45]> Это тоже непрактическая задача. Лучше протестируй скорость вывода 1000-5000 спрайтов (из набора общей площадью хотя бы 1/3 экрана). Вот это будет практическая задача и полезный труд.
Я ж говорил, что ты по-DirectX'овски мыслишь. Ну какой вывод спрайтов на пикселях??? А вот откомпилированное сравнение SpriteUtils с Graphic32 ты мне задолжал.
Ну а заполнение изображения цветом - это обычный Clear. На практике очень часто нужен.
miek (2004-03-26 22:56:00) [46]>была поставлена более практическая задача - заполнить изображение 800х600 жёлтым цветом
Это тоже непрактическая задача. Лучше протестируй скорость вывода 1000-5000 спрайтов (из набора общей площадью хотя бы 1/3 экрана). Вот это будет практическая задача и полезный труд.
dmk (2004-03-26 22:13:00) [47]Так в QPixels каждый раз расчет адреса идет =) Что ж тут удивительного?! А в ScanLine только адрес первого пиксела
расчитывается. Причем наверняка самым простым способом:
DestPtr := BaseAddress + (Y * ScanLineLength).
Ну и далее обращаться к этому адресу как к массиву:
DestPtr[x] := Pixel; или в случае 32 бит так:
Procedure SetDWord(DestPtr,Value:DWord);
asm
mov [eax], edx
end;
Mihey (2004-03-26 21:31:00) [48]Собственно, вопросы. Почему так подкачал QPixels - неужели всё дело в RGB- и TColor-представлении цвета?
Код Scanline:
const
PixelCountMax = 32768;
type
pRGBTripleArray = ^TRGBTripleArray;
TRGBTripleArray = ARRAY[0..PixelCountMax-1] OF TRGBTriple;
var
Bmp: TBitmap;
procedure TmainForm.btnTestClick(Sender: TObject);
var x, y: Integer;
i: Integer;
Row: pRGBTripleArray;
begin
HPCounter.Start;
for y := 0 TO Bmp.Height-1 DO
begin
Row := Bmp.Scanline[y];
for x := 0 TO Bmp.Width-1 DO
begin
with Row[x] do
begin
rgbtRed := 255;
rgbtGreen := 255;
rgbtBlue := 0;
end
end
end;
i := HPCounter.ReadInt;
lblResult.Caption := IntToStr(i);
end;
Код QPixels:
var
Bmp: TBitmap;
QP: TQuickPixels;
procedure TmainForm.btnTestClick(Sender: TObject);
var x, y: Integer;
C: TColor;
i: Integer;
begin
C := clYellow;
HPCounter.Start;
for y := 0 to Bmp.Height-1 do
for x := 0 to Bmp.Width-1 do
begin
QP.setpixel(x,y,c);
end;
i := HPCounter.ReadInt;
lblResult.Caption := IntToStr(i);
end;
Mihey (2004-03-26 21:25:00) [49]Продолжил исследования скорости доступа к пикселам различнми способами, а именно: TCanvas.Pixels, Scanline, QPixels, Graphic32. В этот раз была поставлена более практическая задача - заполнить изображение 800х600 жёлтым цветом (соответствующим константе clYellow илиидентичный ему, т.е. (255, 255, 0) при реализации Scanline и clYellow32 при реализации Graphic32). Тестирование дало весьма интересные результаты.
==========
TCanvas.Pixels
Вполне ожидаемо оказался самым медленным способом. Весь цикл занял 1.58 секунды.
==========
Scanline
Scanline жив! Этот способ показал один из самых лучших результатов - 0.0096 секунды. Сравните эту скорость с TCanvas.Pixels.
==========
QPixels
Неожиданно оказался медленнее Scanline - 0.023 секунды.
==========
Graphic32
Здесь тоже свои ньюансы. Изменение цвета через TBitmap32.Pixel дало результат 0.02 секунды.
Также был использован другой способ, так как задачей стояло залить дёлтым цветом всёизображение, а именно использовался доступ к пикселам через указатели, движение по которым осуществлялось через Inc(). Этот способ показал результат, практически равный Scanline - 0.00958 секунды.
Mihey (2004-03-26 16:03:00) [50]2 miek:
Ну я так и понял в принципе. Собственно, класс TBitmap32 пакета Graphic32 предоставляет доступ к массиву указателей Bits размером Width * Height и аналогично позволяет через Inc(P), где P - указатель на цвет пикселя, двигаться по всему массиву.
miek (2004-03-26 14:23:00) [51]>Расшифруй, а то может твои решения и впрямь лучше.
Ну как же. Берешь адрес пиксела, потом вместо обращения к SetPixel/Pixels[] просто пишешь туда, для изменения координат прост изменяешь адрес.
MBo (2004-03-26 05:40:00) [52]Ясно, что групповая обработка быстрее (о чем я в [3] писал), но реализация ее несравненно сложнее и будет специфичной для каждого отдельного случая. Как, например, проводить аффинное преобразование. Стандартный путь - reverse mapping для того, чтобы не было дырок - обходим новое изображение, ставя каждой его точке в соответствие точку старого. Строки нового сканируем, вычисляем координаты исходных пикселов. Для ускорения можно воспользоваться тем, что прямые при А.П. переходят в прямые - можно использовать брезенхемоподобные алгоритмы. Новая картинка поблочно, последовательно проходится без проблем. Но ведь по старой мы идем наискосок!
dmk (2004-03-25 23:59:00) [53]miek © (25.03.04 23:08) [7]
>Отнюдь нет. Сейчас как раз делаю движок с комбинированным >принципом: рисование софтом в системной памяти, вывод через >BltFast. Проблемы доступа к пикселам вижу. Решаются они не >отдельными процедурами, а групповым доступом.
Абсолютна точно. У меня такая же проблема. Тружусь над
движком такого же плана. Обработка происходит регионами(блоками).
Так получается намного быстрее. Плюс использование перегрузки
функций если это выгодно. Единственный минус по сравнению с
попиксельной обработкой усложнение движка. В моем случае когда он перевалил за 30 тыс. строк, стало почти невозможно все держать в голове.
Mihey (2004-03-25 23:33:00) [54]> Проблемы доступа к пикселам вижу. Решаются они не отдельными процедурами, а групповым доступом.
Расшифруй, а то может твои решения и впрямь лучше.
miek (2004-03-25 23:08:00) [55]>Ну ты наверное мыслишь слишком по-DirectX'стски
Отнюдь нет. Сейчас как раз делаю движок с комбинированным принципом: рисование софтом в системной памяти, вывод через BltFast. Проблемы доступа к пикселам вижу. Решаются они не отдельными процедурами, а групповым доступом.
>Можешь предложить непопиксельный метод для реализации неаффинных преобразований?
А где они нужны, неаффинные?
MBo (2004-03-25 21:06:00) [56]>miek
Можешь предложить непопиксельный метод для реализации неаффинных преобразований?
Mihey (2004-03-25 20:59:00) [57]> И, кстати, нигде он и не нужен особенно.
Ну ты наверное мыслишь слишком по-DirectX'стски, где идёт работа с уже готовыми изображениями спрайтов. Попиксельная работа с изображением требуется и при применении фильтров (3х3, 5х5), и при создании инструментов рисования, и при анализе изображений. А по поводу скорости. Представленные скорости вполне соответсвуют требованиям. Никто ведь не будет выводить просто так растр попиксельно (хотя в том же DirectDraw это, кажется, требуется для достижения alpha-blending'а).
miek (2004-03-25 20:42:00) [58]Камрады! Не поймите неправильно, но доступ к пикселям по одному (всякие SetPixel), какой метод ни используй, быстрым не будет НИГДЕ. Нефиг даже искать. Просто в Дельфи нет макросов, а любой вызов процедуры занимает много времени. Если добавить отсечение в видимой области, то вообще привет.
И, кстати, нигде он и не нужен особенно.
MBo (2004-03-25 18:49:00) [59]Добавлю про QPixels:
Приведенные в статье времена измерены на машине P600, для разной цветовой глубины различие достигает 5 раз (для малоцветных код доступа сложнее). На компе Athlon XP 1900+ различие сильно нивелируется и составляет 2-2.5 (ок.100-240 мегапикселов/сек) - благодаря более эффективной конвейеризации время работы кода существенно быстрее, а лимитирует скорость доступа к памяти, которая возросла не столь значительно. Особенно существенно влияние доступа к памяти при случайных (не последовательных) обращениям к пикселах в разных местах больших битмапов - частично теряются преимущества кэширования. Поэтому при bulk-обработке (посчитать пикселы, найти опред.цвет и т.п.) иногда стоит организовывать "mass production" - считывание и обработку нескольких пикселов сразу, строго последовательное чтение или запись.
Mihey (2004-03-25 17:55:00) [60]Провёл. QPix при работе с 32-bit'ным изображением даёт 0.025 секунды, на 1/1000 меньше.