Rus*_*man 3 c simd pixelformat neon avx2
我有一个大的紧密封装的 12 位整数数组,采用以下重复位封装模式:(其中A n /B n中的n表示位数,A 和 B 是数组中的前两个 12 位整数)
| byte0 | byte1 | byte2 | etc..
| A11 A10 A9 A8 A7 A6 A5 A4 | B11 B10 B9 B8 B7 B6 B5 B4 | B3 B2 B1 B0 A3 A2 A1 A0 | etc..
Run Code Online (Sandbox Code Playgroud)
我将其重新排序为以下模式:
| byte0 | byte1 | byte2 | etc..
| A11 A10 A9 A8 A7 A6 A5 A4 | A3 A2 A1 A0 B11 B10 B9 B8 | B7 B6 B5 B4 B3 B2 B1 B0 | etc..
Run Code Online (Sandbox Code Playgroud)
我使用以下代码使其在每 3 字节循环中工作:
void CSI2toBE12(uint8_t* pCSI2, uint8_t* pBE, uint8_t* pCSI2LineEnd)
{
while (pCSI2 < pCSI2LineEnd) {
pBE[0] = pCSI2[0];
pBE[1] = ((pCSI2[2] & 0xf) << 4) | (pCSI2[1] >> 4);
pBE[2] = ((pCSI2[1] & 0xf) << 4) | (pCSI2[2] >> 4);
// Go to next 12-bit pixel pair (3 bytes)
pCSI2 += 3;
pBE += 3;
}
}
Run Code Online (Sandbox Code Playgroud)
但使用字节粒度对于性能来说并不是很好。目标 CPU 是 64 位 ARM Cortex-A72(Raspberry Pi 计算模块 4)。对于上下文,此代码将 MIPI CSI-2 位打包原始图像数据转换为 Adobe DNG 的位打包。
我希望使用 SIMD 内在函数能够获得相当大的性能提升,但我不太确定从哪里开始。我有 SIMDe 标头来转换内在函数,因此欢迎 AVX/AVX2 解决方案。
NEONld3指令非常适合此目的;它加载 48 个字节并将它们解压缩到三个 NEON 寄存器中。那么你只需要几个轮班和手术室。
我想出了以下几点:
void vectorized(const uint8_t* pCSI2, uint8_t* pBE, const uint8_t* pCSI2LineEnd)
{
while (pCSI2 < pCSI2LineEnd) {
uint8x16x3_t in = vld3q_u8(pCSI2);
uint8x16x3_t out;
out.val[0] = in.val[0];
out.val[1] = vorrq_u8(vshlq_n_u8(in.val[2], 4), vshrq_n_u8(in.val[1], 4));
out.val[2] = vorrq_u8(vshlq_n_u8(in.val[1], 4), vshrq_n_u8(in.val[2], 4));
vst3q_u8(pBE, out);
pCSI2 += 48;
pBE += 48;
}
}
Run Code Online (Sandbox Code Playgroud)
使用 gcc,生成的程序集看起来就像您所期望的那样。(有一个mov可以通过更好的寄存器分配来消除,但这是相当小的。)
不幸的是,clang 有一个看起来奇怪的错过优化,它将 4 位右移分解为 3 位和 1 位移位。 我提交了一个错误。
原则上,我们可以使用 、 Shift Left 和 Insert 做得更好一点sli,以有效地将 OR 与其中一个移位合并:
out.val[1] = vsliq_n_u8(vshrq_n_u8(in.val[1], 4), in.val[2], 4);
out.val[2] = vsliq_n_u8(vshrq_n_u8(in.val[2], 4), in.val[1], 4);
Run Code Online (Sandbox Code Playgroud)
但由于它会覆盖其源操作数,因此我们需要支付几个额外的费用mov。 https://godbolt.org/z/TbzEEd1Pn。clang 分配寄存器更加巧妙,只需要一个mov。
另一种可能稍微快一些的选项是使用sra、右移和累加,它执行添加而不是插入。由于此处相关位已经为零,因此具有相同的效果。奇怪的是没有sla。
out.val[1] = vsraq_n_u8(vshlq_n_u8(in.val[2], 4), in.val[1], 4);
out.val[2] = vsraq_n_u8(vshlq_n_u8(in.val[1], 4), in.val[2], 4);
Run Code Online (Sandbox Code Playgroud)