2015-07-21 93 views
0

我需要計算存儲在一個數組中的32個值的平均值。出於性能原因,我想更改下面的代碼以使用pavgb命令和xmm寄存器。問題是我不能一次使用movdqu複製16字節,因爲我在循環中進行了一些計算以獲得平均值。下面的代碼是我正在使用的實際代碼的簡化版本。按字節填充xmm寄存器

; 
; void average(uint8_t *res, uint8_t *input) 
; rdi = res | res holds 16 values 
; rsi = input | input holds 32 values 
; 
segment .text 
    global average 

average:  
    mov rcx, 0 
    xor rax, rax 
    xor rbx, rbx 
.loop 
    mov al, [rsi + rcx] 
    cmp al, 16 
    jge .endif 
    add al, 16 

    .endif 
    mov bl, [rsi + rcx + 16] 
    cmp bl, 16 
    jge .endif2 
    add bl, 16 

    .endif2 
    add ax, bl 
    shr ax, 1 

    mov [rdi], al 

    inc rdi 
    inc rsi 
    inc rcx 

    cmp rcx, 16 
    jl .loop 

因此,要改變代碼與XMM工作寄存器,所以我可以做這樣的事情到底:

pavgb xmm0, xmm1 
movdqu [rdi], xmm0 

我需要補XMM0與xmm1寄存器按字節。有沒有辦法做到這一點?

+1

您可以使用'PINSRB',可能與'PSLLDQ'結合使用。 – Jester

+0

「res將是輸入值[0]和輸入值[16],輸入值[1]和輸入值[17]的平均值的結果......」您的評論有誤。這使得代碼更難理解,因爲它沒有做出評論所說的話。 –

回答

4

使用pavgb指令沒有任何意義,因爲設置pavgb所需的額外工作遠遠超過首先使用pavgb的性能優勢。你現有的代碼很好。

即使有優化的SSE版本,該函數也很短,以至於性能可能會被函數調用開銷所淹沒。

要獲得性能優勢,您可能需要使用內在函數,以便編譯器可以理解代碼並將其合併到自己的優化中(例如內聯)。

void average(uint8_t *res, uint8_t *input) 
{ 
    auto boundary = __m128i _mm_set1_epi8(0x10); 

    // Process the first half 
    auto part1 = _mm_loadu_si128((__m128i *)input); 
    auto adjust1 = _mm_and_si128(_mm_pcmpgt_epi8(boundary, part1), boundary); 
    auto adjusted1 = _mm_add_epi8(part1, adjust1); 

    // process the second half 
    auto part2 = _mm_loadu_si128((__m128i *)(input + 16); 
    auto adjust2 = _mm_and_si128(_mm_pcmpgt_epi8(boundary, part2), boundary); 
    auto adjusted2 = _mm_add_epi8(part2, adjust2); 

    // average them together 
    auto result = _mm_avg_epu8(adjusted1, adjusted2); 

    // save the answer 
    _mm_storeu_si128((__m128i *)res, result); 
} 

爲了獲得更好的性能,你可能想要的功能,直接返回__m128i這樣,來電者可以用它立即計算,而不是要讀的結果內存不足。