对比度降低 - intel x86
Contrast reduction - intel x86
我应该做一个项目来通过我的课程。我想问一下,是否有可能使我的代码更有效或更好。我这样做是因为我的协调员是一个非常一丝不苟的完美主义者,并且非常追求效率。
这是一个混合程序,它修改 24bpp 位图。这是一个对比度降低,算法看起来像这样(它被我的协调员批准):
comp-=128;
comp*=rfactor
comp/=128
comp+=128
'comp'表示一个像素的每个分量,字面意思是:每个像素中的每个红色、绿色和蓝色的值。
该函数就是这样做的,我使用 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
有什么需要改进的吗?感谢您的所有建议,我必须打动我的协调员 ;)
当不是 up-counting,而是向下计数时,你可以从你的循环中挤出更多一点,就像这样(我改变了 header 来表明意思,我省略了预告片:
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
终于,我成功了。感谢您的所有建议。我希望我的协调员会满意。如果没有,我将被迫使用 vectors
; void contrast(void *img, int pixels, int rfactor);
;堆栈:EBP+8 -> *img
; EBP+12 -> 计数器(修改多少字节)
; EBP+16 -> 因子
节.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
你对 SIMD 版本仍然感兴趣,这里是一个。
它使用 AVX2 指令,因此您至少需要第 4 代处理器(Haswell 微架构)。
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
我已经通过比较它的输出和你的代码的输出来测试它。
C 中的原型是您的旧原型(带有 lineWidth):
void contrast(void* buffer, unsigned int width, unsigned int Bpp, unsigned short rfactor);
我已经在我的机器上做了一些分析。我有 运行 这个版本和你在 2048x20480 图像(120MiB 缓冲区)上的答案中的版本 10 次。你的代码需要 2.93 秒,这个 1.09 秒。虽然这个时间可能不是很准确。
此版本需要的缓冲区大小是 16 的倍数(因为它每个周期处理 16 个字节,一次处理 5 个像素的三分之一),您可以用零填充。如果缓冲区在 16 字节边界上对齐,它将 运行 更快。
如果您想要更详细的答案(例如有用的评论:D),请在评论中提问。
编辑:在 Peter Cordes 的伟大帮助下更新了代码,以供将来参考。
我应该做一个项目来通过我的课程。我想问一下,是否有可能使我的代码更有效或更好。我这样做是因为我的协调员是一个非常一丝不苟的完美主义者,并且非常追求效率。 这是一个混合程序,它修改 24bpp 位图。这是一个对比度降低,算法看起来像这样(它被我的协调员批准):
comp-=128;
comp*=rfactor
comp/=128
comp+=128
'comp'表示一个像素的每个分量,字面意思是:每个像素中的每个红色、绿色和蓝色的值。 该函数就是这样做的,我使用 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
有什么需要改进的吗?感谢您的所有建议,我必须打动我的协调员 ;)
当不是 up-counting,而是向下计数时,你可以从你的循环中挤出更多一点,就像这样(我改变了 header 来表明意思,我省略了预告片:
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
终于,我成功了。感谢您的所有建议。我希望我的协调员会满意。如果没有,我将被迫使用 vectors
; void contrast(void *img, int pixels, int rfactor);
;堆栈:EBP+8 -> *img
; EBP+12 -> 计数器(修改多少字节)
; EBP+16 -> 因子
节.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
你对 SIMD 版本仍然感兴趣,这里是一个。
它使用 AVX2 指令,因此您至少需要第 4 代处理器(Haswell 微架构)。
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
我已经通过比较它的输出和你的代码的输出来测试它。
C 中的原型是您的旧原型(带有 lineWidth):
void contrast(void* buffer, unsigned int width, unsigned int Bpp, unsigned short rfactor);
我已经在我的机器上做了一些分析。我有 运行 这个版本和你在 2048x20480 图像(120MiB 缓冲区)上的答案中的版本 10 次。你的代码需要 2.93 秒,这个 1.09 秒。虽然这个时间可能不是很准确。
此版本需要的缓冲区大小是 16 的倍数(因为它每个周期处理 16 个字节,一次处理 5 个像素的三分之一),您可以用零填充。如果缓冲区在 16 字节边界上对齐,它将 运行 更快。
如果您想要更详细的答案(例如有用的评论:D),请在评论中提问。
编辑:在 Peter Cordes 的伟大帮助下更新了代码,以供将来参考。