对比度降低 - 英特尔x86

时间:2015-06-14 18:31:16

标签: assembly bitmap x86 intel

我应该做一个项目来通过我的课程。我想问一下,如果有可能让我的代码更有效或更好。我之所以这样做,是因为我的协调员是一位非常细致的完美主义者,对效率很着迷。 它是一个混合程序,它修改了一个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 

有什么需要改进的吗?谢谢你的所有建议,我必须给我的协调员留下深刻的印象;)

3 个答案:

答案 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