我想将8位整数转换为大小为8的数组,每个值都包含整数的位值。
例如:我有int8_t x = 8;
,我想将其转换为int8_t array_x = {0,0,0,0,1,0,0,0};
由于此计算是信号处理模块的一部分,因此必须有效地完成此操作。有有效的方法吗?我确实检查了混合说明。当具有8位大小的数组元素时,它不符合我的要求。开发平台是AMD Ryzen。
答案 0 :(得分:3)
具有0x00:0x01
格式结果的单个字节的“反向移动掩码”,带有SIMD,但没有BMI2。
__m128i v = _mm_set1_epi8(bitmap);
v = _mm_and_si128(v, _mm_set_epi32(0, 0, 0x80402010, 0x08040201));
v = _mm_min_epu8(v, _mm_set1_epi8(1));
_mm_storel_epi64((__m128i*)&array_x[0], v);
答案 1 :(得分:1)
此答案末尾的第一个示例显示了如何使用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]);
}
}