2015-06-14 3 views
0

Я должен был сделать проект, чтобы пройти мой курс. Я хотел бы спросить, есть ли возможность сделать мой код более эффективным или просто лучше. Я делаю это, потому что мой координатор - очень тщательный перфекционист и сумасшедший об эффективности. Это гибридная программа, она изменяет растровое изображение 24bpp. Это уменьшение контрастности, алгоритм выглядит следующим образом (он утвержден моим координатором):Уменьшение контрастности - intel x86

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

Вы можете использовать расширения, такие как SSE 4.x и/или AVX? –

+0

@ knm241 да, но я понятия не имею, как его использовать в этом случае –

+0

Я не уверен, что стоит использовать SSE4.x/AVX, поскольку они требуют тестирования последнего процессора. Я написал эскиз кода с использованием SIMD-указаний, нужно сделать несколько тестов. Во всяком случае, какую-то микро-оптимизацию вы могли бы сделать: 1) Используйте инструкцию 'loop' вместо' jnz loop', поэтому вы можете избежать 'dec ecx'. 2) не нуль eax 3) перепишите свое уравнение из 'out = (in-128) * r/128 + 128' в' out = (in * r >> 7) + 128 - r', чтобы вы могли предварительно компилировать ' 128-r' перед циклом и упростить цикл (теперь должно быть возможно использовать 'shr', поскольку' in * r' является положительным. 4) Разверните цикл, чтобы обработать 4 пикселя за один раз, загружая их вместе. –

ответ

1

Я вас по-прежнему заинтересованы в версии 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 

Я тестировал его, сравнивая его выходные данные с выходом вашего кода.

Прототип в С старой один (с LineWidth):

void contrast(void* buffer, unsigned int width, unsigned int Bpp, unsigned short rfactor); 

Я сделал некоторые профилирования на моей машине. Я запустил эту версию, а другой в вашем ответе на изображение 2048x20480 (буфер 120 Мбайт) 10 раз. Ваш код занимает 2,93 секунды, это 1.09 секунды. Хотя эти тайминги могут быть не очень точными.

Эта версия требует размера буфера, который кратен 16 (поскольку он обрабатывает 16 байтов за такт, 5 и одну треть пикселей за раз), вы можете использовать нули. Если буфер выравнивается по 16-байтовым границам, он будет работать быстрее.

Если вы хотите получить более подробный ответ (с полезными комментариями, например: D), просто спросите в комментариях.

EDIT: Обновлен код с большими помощью Питера Корда, для дальнейшего использования.

+0

Думаю, я справимся. Огромное спасибо! –

+0

Почему вы используете 'vinserti128' дважды? Первым может быть 'vmovdqu', которому не нужен ни один порт 015 uops (и нарушает любую зависимость от старого значения' ymm' reg.) Также вы можете просто использовать 'vpbroadcastd' для трансляции 32-битного значения в все элементы 'ymm', а не 4x' push'. Для магазина 'vextracti128 [esp], ymm0, 0' на 100% эквивалентен' vmovdqu [esp], xmm0'. Я думаю, что он существует (вместо storehigh128), поэтому он будет работать одинаково в avx-512, где imm8 будет индексировать одну из 4 возможных 128 дорожек. –

+0

также, вместо последовательности 'lodsd' /' stosb', вы не могли бы сдвинуть каждый другой байт с чем-то вроде 'vpsrlw', а затем' vpblendsomething', чтобы в итоге появился регистр, содержащий требуемую комбинацию смещенных байтов и не сдвинутые байты? И используйте 'vpackuswb', чтобы вернуться на 8-битные полосы, с 16 бит. –

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 
0

Наконец, мне удалось Спасибо за все ваши предложения, я надеюсь, что мой координатор будет удовлетворен Если нет, то я был бы вынужден сделать это с помощью векторов
, недействительным контраст (недействительный * img, int pixels, int rfactor); ; стек: EBP + 8 -> * img ; EBP + 12 -> счетчик (сколько байтов для изменения) ; EBP + 16 -> RFactor секция .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 
+0

На процессорах Intel до Nehalem, которые 16 бит 'shr' будет медленно декодировать каждый раз через цикл. (более поздние процессоры имеют буфер цикла, достаточно большой, чтобы удерживать все вершины для этого небольшого цикла.) –

+0

Нет смысла писать непосредственно в asm, если вы не собираетесь использовать векторные инструкции, особенно. для чего-то вроде графики, ИМО. Является ли ваша версия asm намного быстрее, чем компилятор C? –

Смежные вопросы