Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

В поисках пикселя - benchmark [D5, Win95/98]


Страницы:   1 2 3 4

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 меньше.


Страницы:   1 2 3 4

Наверх

Титульная страница Поиск, карта сайта Написать письмо