Имеем A - dword RGB цвет (00000000RRRRRRRRGGGGGGGGBBBBBBBB) Нужно посчитать B byte = (((R * 77) + (G * 151) + (B * 28)) / 256) Решение: 1) C: (для образца) B = (((A.rgbRed * 77) + (A.rgbGreen * 151) + (A.rgbBlue * 28)) >> 8) & 255; 2а) asm: (мой вариант c imul) Code (Text): mov edx, A.Value movzx eax, dl movzx ecx, dh shr edx,16 imul eax,eax,28 imul ecx,ecx,151 imul edx,edx,77 add eax, ecx add eax, edx sar eax, 8 mov B, al 2b) asm: (мой вариант c shl/shr) Code (Text): mov edx, A.Value movzx ecx, dl movzx eax, dl // 1 (B 28) shl eax,3 // 8 sub eax,ecx // 7 (-) shl eax,2 // 28 mov ebx,eax // store movzx ecx, dh movzx eax, dh // 1 (G 151) shl eax,2 // 4 add eax,ecx // 5 (+) shl eax,2 // 20 sub eax,ecx // 19 (-) shl eax,3 // 152 sub eax,ecx // 151 (-) add ebx,eax // store shr edx, 8 movzx ecx, dh movzx eax, dh // 1 (R 77) shl eax,2 // 4 add eax,ecx // 5 (+) shl eax,2 // 20 sub eax,ecx // 19 (-) shl eax,2 // 76 add eax,ecx // 77 (+) add eax, ebx // store all sar eax, 8 mov B, al 1. А быстрее можно ( чем asm: ) ? Оптимизация нужна под Pentium, P!! - P!!! - PIV, т.е. более менее универсальная. Правда если есть "частные" случаи оптимизации тоже будет интересно. Пробовалось заменить imul на shr/shl - выигрыша не было (см. пример). 2. По ходу вопрос как мерять производительность выполения кода функцией Win32 ? QueryPerformanceCounter()? GetTickCount()?
Числа небольшие, можно табличку составить на 256*3*2 байт. Если жалко килобайта, можно динамически ее заполнить.
CL /Ox предлагает такое (вход в ecx): Code (Text): mov eax, ecx shr eax, 8 movzx edx, al mov eax, ecx imul edx, 151 shr eax, 16 movzx eax, al movzx ecx, cl imul ecx, 77 push esi lea esi, DWORD PTR [eax*8] sub esi, eax lea eax, DWORD PTR [edx+esi*4] add eax, ecx cdq and edx, 255 add eax, edx sar eax, 8 movzx eax, al pop esi
Code (Text): .DATA Val_77_151_28 DQ 0000004D0097001Ch Val_000000000000FF DQ 00000000000000FFh .CODE ... movd MM0, eax pxor MM1, MM1 punpcklbw MM0, MM1 pmullw MM0, Val_77_151_28 movq MM1, MM0 movq MM2, MM0 psrlq MM0, 8 psrlq MM1, 24 psrlq MM2, 40 pand MM0, Val_000000000000FF pand MM1, Val_000000000000FF paddusw MM0, MM1 paddusw MM0, MM2 movd eax, MM0 ...
NoResponse Не правильно делать таким способом. При сдвиге, который используется в примере (psrlq mm0, 8 и др.) теряются младшие 8 бит произведения, что влияет на сумму в итоге.
reverver Ваш вариант медленнее в 1.2375 раза чем мой 2a) Интересно, какой CL/MSVC вы использовали? По совету KeSqueer появился вариант с таблицей. Вышло быстрее варианта 2a) в 1.5 раза. Другие оптимизации медленнее этой в 1.75 - 2 раза. Code (Text): ... unsigned short vv, rv[256],gv[256],bv[256]; ... ... for (vv = 0; vv < 256 ; vv++) // fill tables :) { rv[vv] = vv * 77; gv[vv] = vv * 151; bv[vv] = vv * 28; } ... ... mov edx, A.Value movzx eax, dl movzx ecx, dh shr edx,16 movzx eax,word ptr [bv + eax*2] movzx ecx,word ptr [gv + ecx*2] movzx edx,word ptr [rv + edx*2] add eax,ecx add eax,edx sar eax, 8 mov B, al А такой вариант выдал Intel C++ 7.0 ( icl.exe /Ox ) : Code (Text): mov eax,A.uValue movzx edi,al mov edx,eax shr edx,000000010 ;' ' movzx ecx,dl lea edx,[ecx][ecx] add edx,edx sub edx,ecx add edx,edx add edx,edx sub edx,ecx lea ecx,[edx][edx] add ecx,ecx add ecx,ecx sub ecx,edx shr eax,8 movzx edx,al lea eax,[edx][edx]*4 add eax,eax add eax,eax sub eax,edx add eax,eax add eax,eax add eax,eax sub eax,edx add ecx,eax add edi,edi add edi,edi lea eax,[edi][edi] add eax,eax add eax,eax sub eax,edi add ecx,eax sar ecx,8 mov B, cl На P4 он работал шустрее 2а) до тех пор пока не появился вариант с таблицей.
Code (Text): punpcklbw mm1,[ARGB_32bit] psrlw mm1,8 pmaddwd mm1,[Scale_0_77_151_28] movq mm2,mm1 psrlq mm1,32 paddd mm2,mm1 psrld mm2,8 movd eax,mm2 для sse тоже самое,толь константу увеличиваем *2, не забывая про 0 вместо альфа компоненты, для зануления при умножении и двигаемся на каждой итерации не на 4, а на 8, т.е. 2 пиксела за раз и это без разроливания (unroll)
При переводе цвета в монохром в одной из своих прог использовал вот такой кусок кода. Не помню как мне это пришло на ум, и комменты я не силен писать. Могу только сказать, что реально работало. Скорость не тестировал. Исходный цвет в eax == A.Value результат в BL ;<32to8 mov ecx, eax and eax, 000ff00ffh mov ebx, 0001D004Ch mul ebx and eax, 0FFFFh add edx, eax mov eax, ecx mov ah, mul ah add dx, ax mov bl, dh ;>32to8
В итоге на P-4 вариант от S_Alex оказался медленнее табличного в 1.5 раза, а MMX-вариант от asmfan - в 2 раза.
А так, видимо, короче но чуть медленнее (таблица выравнена по dword). Code (Text): ... unsigned long vv, rv[256],gv[256],bv[256]; ... ... for (vv = 0; vv < 256 ; vv++) // fill tables :) { rv[vv] = vv * 77; gv[vv] = vv * 151; bv[vv] = vv * 28; } ... ... mov edx, A.Value movzx eax, dl movzx ecx, dh shr edx,16 mov eax,dword ptr [bv + eax*4] add eax,dword ptr [gv + ecx*4] add eax,dword ptr [rv + edx*4] sar eax, 8 mov B, al
asmfan 8192 пикселов т.е. разных значений ARGB из труколорной текстуры в оперативной памяти PC. А таблица ведь один раз заполняется и всё. Цель затеи - перегнать 32/24-bit картинку x*y т.е. любого размера ( правда x всегда кратен 8-ми ) в 8-bit greyscale максимально быстрым методом.
В смысле? пикселов? или повторения над одним и тем же пикселом циклов? Если синтетика, то не тадо брать такие большие значения ибо переключения контекстов, если вживую на картинке, то это реальнее. По идее табличный метод должне выиграть на достаточно больших объёмах т.к. заполнение таблицы займет сравнительно меньшее время чем остальные операции, можно ещё ускориться - не заполнять таблицу, а хранить её как статические данные. 3KB можно себе позволить если скорость критична.
там про просто Pentium было написано. Тогда точно так быстрее будет (для 2х пикселов сразу). Для Pentium II + не уверен, но, думаю, не хуже. mov edx, A.Value1 mov ebx, A.Value2 movzx eax,dl movzx ecx,bl movzx esi, dh movzx edi, bh shr edx,16 shr ebx,16 mov ax,word ptr [bv + eax*2] mov cx,word ptr [bv + ecx*2] add ax,word ptr [gv + esi*2] add cx,word ptr [gv + edi*2] add ax,word ptr [rv + edx*2] add cx,word ptr [rv + ebx*2] mov B1, ah mov B2, ch
Ещё, уже точно не помню, но вполне возможно, что movzx в первом пентиуме была микрокодовой командой (несколько тактов без распараллеливания). Тогда начало надо переписать, например, так: mov edx,A.Value1 mov ebx,A.value2 xor eax,eax xor ecx,ecx xor esi,esi xor edi,edi mov al,dl mov cl,bl mov si,dx mov di,bx shr esi,8 shr edi,8 Вообще, сколь интересно было оптимизировать под первый пентиум. Когда я понял, что почти все методы во втором пентиуме не работают, я практически перестал программировать на асме.