本答案末尾的第一个示例显示了如何使用 BMI2 指令 pdep 来计算 8 字节数组。
请注意,在 Intel Haswell 处理器和更新版本上,pdep 指令的吞吐量为 1
每个周期的指令和 3 个周期的延迟,速度很快。在 AMD Ryzen 上,这条指令是
不幸的是相对较慢:延迟和吞吐量都是 18 个周期。
对于 AMD Ryzen,最好将 pdep 指令替换为乘法和一些按位运算,这在 AMD Ryzen 上相当快,请参阅此答案末尾的第二个示例。
另见here 和here
用于高效的反向移动掩码计算,具有标量源
和一个 256 位 AVX2 矢量目标。
而不是当时使用 8 位和 8 字节,它可能是
更有效地重新组织您的算法以每步使用 4 x 8 位和 4 x 8 字节。
在这种情况下,可以使用 256 位的完整 AVx2 矢量宽度,这可能会更快。
Peter Cordes shows 认为 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]);
}
}