Векторная инструкция Intel для расширения нуля до 8 4-битных значений, упакованных в 32-битное целое число до __m256i? - PullRequest
3 голосов
/ 10 марта 2019

, как говорится в вопросе, у меня есть обычный int, который состоит из 8 упакованных значений по 4 бита каждое, и я хотел бы расширить это значение на ноль в 256-битный векторный регистр.Это возможно с sse / avx / avx2?

Ответы [ 2 ]

3 голосов
/ 11 марта 2019

Решение от chtz (в остальном называется cvt_nib_epi32_chtz) очень подходит для общих целей.Однако в некоторых конкретных случаях представленные ниже решения могут быть несколько более эффективными:

/*     gcc -O3 -m64 -Wall -march=skylake cvt_nib_epi32.c     */
#include <immintrin.h>
#include <stdio.h>
#include <stdint.h>

__m256i cvt_nib_epi32_SKL(uint32_t x) {                /* Efficient on Intel Skylake and newer         */
                                                       /* Broadcast x to 8 elements                    */
    __m256i input   = _mm256_set1_epi32(x);            
                                                       /* Shift the nibbles to the right position      */
    __m256i shifted = _mm256_srlv_epi32(input,_mm256_set_epi32(28,24,20,16,12,8,4,0)); 
                                                       /* Mask off the unwanted bits and return        */
            return _mm256_and_si256(shifted, _mm256_set1_epi32(0xF)); 
}


__m256i cvt_nib_epi32_HSW(uint32_t x) {                /* Efficient on intel Haswell and Broadwell     */
                                                       /* Very inefficient in AMD Zen!                 */
    __uint64_t x_b = _pdep_u64(x, 0x0F0F0F0F0F0F0F0F); /* Expand nibbles to bytes                      */
    __m128i    x_v = _mm_cvtsi64_si128(x_b);           /* Move x_b from GPR to AVX vector register     */
    return _mm256_cvtepu8_epi32(x_v);                  /* Convert bytes to integer elements and return */
} 

Следующая сборка , сгенерированная gcc :

cvt_nib_epi32_SKL:
        vmovd   xmm0, edi
        vpbroadcastd    ymm0, xmm0
        vpsrlvd ymm0, ymm0, YMMWORD PTR .LC0[rip]
        vpand   ymm0, ymm0, YMMWORD PTR .LC1[rip]
        ret
cvt_nib_epi32_HSW:
        movabs  rax, 1085102592571150095
        mov     edi, edi
        pdep    rdi, rdi, rax
        vmovq   xmm0, rdi
        vpmovzxbd       ymm0, xmm0
        ret
cvt_nib_epi32_chtz:
        vmovd   xmm0, edi
        vpsrld  xmm1, xmm0, 4
        vpunpcklbw      xmm0, xmm0, xmm1
        vpand   xmm0, xmm0, XMMWORD PTR .LC2[rip]
        vpmovzxbd       ymm0, xmm0
        ret

Функция cvt_nib_epi32_chtz очень подходит для микроархитектуры AMD zen, поскольку она не использует инструкции pdep иvpsrlvd, которые медленны на этих процессорах.

На процессорах Intel cvt_nib_epi32_chtz может страдать от высокого давления порта 5 (p5), в зависимости от окружающего кода, потому что vmovd, vpunpcklbwvpmovzxbd, все выполняются на p5.Другие функции декодируют только до 2 p5 моп.

В решении Skylake cvt_nib_epi32_SKL используется vpsrlvd, что медленно для Intel Haswell и Broadwell.Для этих процессоров cvt_nib_epi32_HSW подходит.Он использует инструкцию BMI2 pdep, которая очень (!) Медленна на микроархитектуре AMD Zen.Обратите внимание, что cvt_nib_epi32_HSW также должен хорошо работать на Intel Skylake, но (опять же) фактическая производительность зависит от окружающего кода.

Обратите внимание, что в контексте цикла постоянная загрузка, такая как YMMWORD PTR .LC0[rip] и movabs rax, 1085102592571150095, вероятно, поднят из петли.В этом случае требуется всего 4 мопа для cvt_nib_epi32_HSW и cvt_nib_epi32_SKL.

2 голосов
/ 10 марта 2019

Вот решение, которое должно поддерживать порядок:

__m256i foo(int x) {
    __m128i input = _mm_cvtsi32_si128(x);
    __m128i even  = input;
    // move odd nibbles to even positions:
    __m128i odd   = _mm_srli_epi32(input,4);
    // interleave: (only lower 64bit are used)
    __m128i inter = _mm_unpacklo_epi8(even, odd);
    // mask out wrong nibbles:
    __m128i masked = _mm_and_si128(inter, _mm_set1_epi32(0x0f0f0f0f));
    // convert to 32bit:
    return _mm256_cvtepu8_epi32(masked);
}

Годболт-ссылка: https://godbolt.org/z/8RLUVE

Вы могли бы стать немного эффективнее, если бы загрузить два или четыре int32сразу для чередования и маскирования четных и нечетных кусков.(Конечно, это приведет к нескольким __m256i векторам)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...