Первый пример в конце этого ответа показывает, как использовать инструкцию BMI2 pdep
для вычисления массива из 8 байтов.
Обратите внимание, что на процессорах Intel Haswell и новее пропускная способность pdep
равна единице.
инструкция за цикл и задержка 3 цикла, что быстро. На AMD Ryzen эта инструкция есть
к сожалению, относительно медленно: и задержка, и пропускная способность составляют 18 циклов.
Для AMD Ryzen лучше заменить инструкцию pdep
на умножение и несколько побитовых операций, которые выполняются на AMD Ryzen довольно быстро, см. Второй пример в конце этого ответа.
<Ч />
См. Также здесь и здесь
для эффективных вычислений обратной маски движения со скалярным источником
и 256-битный AVX2-вектор назначения.
Вместо того, чтобы работать с 8 битами и 8 байтами одновременно, это может быть
более эффективно реорганизовать ваш алгоритм для работы с 4 x 8 битами и 4 x 8 байтами на шаг.
В этом случае может использоваться полная ширина вектора AVx2 256 бит, что может быть быстрее.
Питер Кордес показывает , что инструкция pext
может использоваться для преобразования в
обратное направление: от 8 байтов до 8 бит.
<Ч />
Пример кода с инструкцией pdep
:
/* gcc -O3 -Wall -m64 -march=skylake bytetoarr.c */
#include<stdint.h>
#include<stdio.h>
#include<x86intrin.h>
int main(){
int i;
union {
uint8_t a8[8];
uint64_t a64;
} t;
/* With mask = 0b0000000100......0100000001 = 0x0101010101010101 */
/* the input bits 0, 1, ..., 7 are expanded */
/* to the right positions of the uint64_t = 8 x uint8_t output */
uint64_t mask = 0x0101010101010101;
/* example input: */
uint8_t x = 0b01001100;
t.a64 = _pdep_u64(x,mask);
for (i = 0; i < 8; i++){
printf("a[%i] = %hhu\n", i, t.a8[i]);
}
}
Вывод:
$ ./a.out
a[0] = 0
a[1] = 0
a[2] = 1
a[3] = 1
a[4] = 0
a[5] = 0
a[6] = 1
a[7] = 0
<Ч />
Пример кода для процессоров AMD Ryzen:
/* gcc -O3 -Wall -m64 -march=skylake bytetoarr_amd.c */
#include<stdint.h>
#include<stdio.h>
#include<x86intrin.h>
int main(){
int i;
union {
uint8_t a8[8];
uint64_t a64;
} t;
/* example input: */
uint8_t x = 0b01001100;
uint64_t x64 = x;
uint64_t x_hi = x64 & 0xFE; /* Unset the lowest bit. */
uint64_t r_hi = x_hi * 0b10000001000000100000010000001000000100000010000000; /* Copy the remaining 7 bits 7 times. */
uint64_t r = r_hi | x64; /* Merge the lowest bit into the result. */
t.a64= r & 0x0101010101010101 ; /* Mask off the bits at the unwanted positions. */
for (i = 0; i < 8; i++){
printf("a[%i] = %hhu\n", i, t.a8[i]);
}
}