我应该做一个项目来通过我的课程。我想问一下,如果有可能让我的代码更有效或更好。我之所以这样做,是因为我的协调员是一位非常细致的完美主义者,对效率很着迷。 它是一个混合程序,它修改了一个24bpp的位图。这是一个对比度降低,算法看起来像这样(它由我的协调员批准):
comp-=128;
comp*=rfactor
comp/=128
comp+=128
'排版'表示像素的每个分量,字面意思是:每个像素中的红色,绿色和蓝色的每个值。 函数就是这样做的,我使用C中的其他函数从文件中读取。我转发组装一个包含组件的数组,bmp的宽度,每行中的像素数量,以及' rfactor' - 对比度降低的价值。然后我就这样做了:
; void contrast(void *img, int width, int lineWidth, int rfactor);
; stack: EBP+8 -> *img
; EBP+12 -> width [px]
; EBP+16 -> lineWidth [B]
; EBP+20 -> rfactor (values in range of 1-128)
section .text
global contrast
contrast:
push ebp
mov ebp, esp
push ebx
mov ebx, [ebp+12] ; width
mov eax, [ebp+16] ; lineWidth
mul ebx ; how much pixels to reduce
mov ecx, eax ; set counter
mov edx, [ebp+8] ; edx = pointer at img
mov ebx, [ebp+20] ; ebx=rfactor
loop:
xor eax, eax
dec ecx ; decrement counter
mov al, [edx] ; current pixel to al
add eax, -128
imul bl ; pixel*rfactor
sar eax, 7 ; pixel/128
add eax, 128
mov byte[edx], al ; put the pixel back
inc edx ; next pixel
test ecx, ecx ; is counter 0?
jnz loop
koniec:
pop ebx
mov esp, ebp
pop ebp
ret
有什么需要改进的吗?谢谢你的所有建议,我必须给我的协调员留下深刻的印象;)
答案 0 :(得分:1)
I you are still interested in a SIMD version here is one.
It use AVX2 instructions so you need at least a 4th generation processor (Haswell micro-architecture).
BITS 32
GLOBAL _contrast
SECTION .code
;rfactor
;lineWidth
;width
;ptr to buffer
_contrast:
push ebp
mov ebp, esp
and esp, 0fffffff0h
push edi
push esi
push ebx
push eax
mov eax, DWORD [ebp+0ch] ;witdh
mul DWORD [ebp+10h] ;total bytes
mov ecx, eax ;Number of bytes to process
shr ecx, 04h ;Process chunks of 16 bytes per cycle
mov edi, DWORD [ebp+08h] ;Buffer
;--- Prepare ymm registers ---
vzeroall
sub esp, 10h
;ymm1 contains the r factor (x16)
movzx ebx, WORD [ebp+14h]
mov DWORD [esp], ebx
vpbroadcastw ymm1, WORD [esp] ;ymm1 = r (x16)
;ymm0 contains the 128-r value (x16)
neg WORD [esp] ;-r
mov al, 128
add WORD [esp], ax ;128-r
vpbroadcastw ymm0, WORD [esp] ;ymm0 = 128-r (x16)
add esp, 10h
.loop:
;Computer channels values
vpmovzxbw ymm2, [edi] ;16 channels (128 bit) to 16 words
vpmullw ymm2, ymm2, ymm1 ;ymm2 = in*r
vpsrlw ymm2, ymm2, 7 ;ymm2 = in*r>>7
vpaddw ymm2, ymm2, ymm0 ;ymm2 = in*r>>7 + r-128
vpackuswb ymm2, ymm2, ymm2 ;xmm2 = 16 computes values
;Store to memory
movdqa [edi], xmm2
add edi, 10h
loop .loop
pop eax
pop ebx
pop esi
pop edi
mov esp, ebp
pop ebp
ret
I have tested it by comparing its output with the output of your code.
The prototype in C is your old one (with lineWidth):
void contrast(void* buffer, unsigned int width, unsigned int Bpp, unsigned short rfactor);
I have done some profiling on my machine. I have run this version and the one in your answer on a 2048x20480 image (120MiB buffer) 10 times. Your code takes 2.93 seconds, this one 1.09 seconds. Though this timings may not be very accurate.
This version require a buffer size that is a multiple of 16 (because it processes 16 bytes per cycle, 5 and one third of pixel at a time), you can pad with zeros. If the buffer is aligned on 16 byte boundaries it will run faster.
If you want a more detailed answer (with useful comments for example :D) just ask in the comments.
EDIT: Updated the code with the great help of Peter Cordes, for future reference.
答案 1 :(得分:0)
你可以在没有向上计数的情况下从你的循环中挤出一点,但是向下计数,就像这样(我更改了标题以表示含义,我省略了预告片:
mov edx, img
mov ecx, width*lineWidth ; pseudo-assembler here
mov ebx, rfactor
loop:
xor eax, eax
mov al, [ecx+edx-1]
sub eax, 128
imul bl
sar eax, 7
add eax, 128
mov byte ptr[ecx+edx-1], al
dec ecx
jnz loop
; add trailer here
答案 2 :(得分:0)
最后,我成功了。感谢您的所有建议。我希望我的协调员会满意。如果没有,我将被迫使用矢量
做
; void contrast(void * img,int pixels,int rfactor);
;堆栈:EBP + 8 - > * IMG
; EBP + 12 - >计数器(要修改的字节数)
; EBP + 16 - > rfactor
section .text
全球对比
contrast:
push ebp
mov ebp, esp
push ebx
push edi
mov ecx, [ebp+12] ; set counter
mov ebx, [ebp+16] ; rfactor to ebx
mov edi, [ebp+8] ; img pointer
mov dl, 128
sub dl, bl ; 128-rfactor
loop:
mov al, [edi] ; current pixel to al
mul bl ;
shr ax, 7 ; byte*rfactor>>7+(128-rfactor)
add al, dl ;
stosb ; store al, inc edi
loop loop
koniec:
pop edi
pop ebx
mov esp, ebp
pop ebp
ret