2015-07-21 5 views
0

Мне нужно рассчитать среднее значение 32 uint8t значений, хранящихся в одном массиве. По соображениям производительности я хотел изменить код ниже, чтобы использовать команду pavgb и регистры xmm. Проблема в том, что я не могу скопировать 16 байт сразу с помощью movdqu, потому что я делаю некоторые вычисления в цикле, чтобы получить значения в среднем. Код ниже представляет собой упрощенную версию фактического кода, который я использую.Заполнение xmm register bytewise

; 
; 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 непосредственно, так что абонент может вычислить с ним сразу, а не должна прочитать результат из памяти.

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