Решение от 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
, vpunpcklbw
,и vpmovzxbd
, все выполняются на 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
.