【发布时间】:2020-07-31 22:55:34
【问题描述】:
我有一个 16384 有符号四位整数的输入向量。它们被打包成 8192 字节。我需要将这些值交错并解压缩为两个单独数组中的有符号 8 位整数。
a,b,c,d 是 4 位值。
A,B,C,D 是 8 位值。
输入 = [ab,cd,...]
Out_1 = [A,C, ...]
Out_2 = [B,D, ...]
我可以在 C++ 中很容易地做到这一点。
constexpr size_t size = 32768;
int8_t input[size]; // raw packed 4bit integers
int8_t out_1[size];
int8_t out_2[size];
for (int i = 0; i < size; i++) {
out_1[i] = input[i] << 4;
out_1[i] = out_1[i] >> 4;
out_2[i] = input[i] >> 4;
}
我想实现它以在通用处理器上尽可能快地运行。在 VOLK 中存在 8 位解交织到 16 位整数的良好 SIMD 实现,但我什至找不到基本的按字节 SIMD 移位运算符。
https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_8ic_deinterleave_16i_x2.h#L63
谢谢!
【问题讨论】:
-
x86 SIMD 没有字节移位,您必须通过 16 位或更宽的移位来模拟它们,并屏蔽掉进入每个字节顶部的位 (SSE/SIMD shift with one-byte element size / granularity?)。当您希望算术移位进行符号扩展时,这种情况很糟糕;也许设置高位的不同技巧可以用于固定移位计数。就像
xor和0xf8设置高位并翻转第 4 位,然后paddb和0x08将更正第 4 位,或者执行并清除高位,或者保留它们。跨度> -
等等,你的问题说你需要签名,但是你的 C++ 使用
uint8_t来处理所有事情,而不是int8_t。无符号更容易,只需移位和掩码。 (即使有字节移位,低半部分移位两次也是低效的;AND 与_mm_set1_epi8(0x0f)) -
用这个想法更新了SSE/SIMD shift with one-byte element size / granularity?:4 uop(对于英特尔)比以前模拟不存在的
psrab(_mm_srai_epi8) 要好。 -
使用
uint8_t input,您的out_2结果仍然不正确。 (零扩展而不是符号扩展。)您可以将其设为int8_t*,或将其转换为((int8_t)input[i]) >> 4。这实际上是自动矢量化的,clang 相当好,GCC 相当差:godbolt.org/z/zYhff7