【发布时间】:2018-04-19 10:25:59
【问题描述】:
我有一个循环进行一些计算,然后将符号位存储到一个向量中:
uint16x8_t rotate(const uint16_t* x);
void compute(const uint16_t* src, uint16_t* dst)
{
uint16x8_t sign0 = vmovq_n_u16(0);
uint16x8_t sign1 = vmovq_n_u16(0);
for (int i=0; i<16; ++i)
{
uint16x8_t r0 = rotate(src++);
uint16x8_t r1 = rotate(src++);
// pseudo code:
sign0 |= (r0 >> 15) << i;
sign1 |= (r1 >> 15) << i;
}
vst1q_u16(dst+1, sign0);
vst1q_u16(dst+8, sign1);
}
在伪代码之后的 neon 中累积符号位的最佳方法是什么?
r0 = vshrq_n_u16(r0, 15);
r1 = vshrq_n_u16(r1, 15);
sign0 = vsraq_n_u16(vshlq_n_u16(r0, 15), sign0, 1);
sign1 = vsraq_n_u16(vshlq_n_u16(r1, 15), sign1, 1);
另外,请注意,“伪代码”实际上可以正常工作,并且生成几乎相同的代码。这里有什么可以改进的?注意,在实际代码中,循环中没有函数调用,我精简了实际代码以使其易于理解。
另一点:在霓虹灯中,您不能使用变量进行矢量移位(例如,i 不能用于指定移位次数)。
【问题讨论】:
-
vsraq是算术移位,而不是逻辑移位?为什么用那个?此外,如果在移位前使用 AND 将非符号位归零,则可以使用更少的移位。比如sign0 |= (r0 & 0x8000) >> (15-i);或者固定班次:sign0 |= (r0 & 0x8000); sign0 >>= 1;后者用SIMD实现应该很简单高效,但我不太了解ARM。 -
VSRA 是向量右移立即值和累加。
_u16与_s16将定义逻辑与算术移位。 -
哦,所以你用
(x >> 15) << 15而不是x & 0x8000来隔离符号位。看起来您可以使用vsra轻松实施我之前评论中的第二条建议:tmp = r0 & 0x8000; sign0 = (sign0 >> 1) + tmp; -
for & 0x8000 我需要创建一个 q 寄存器,但我不确定我是否有足够的循环。我会试试看我是否能得到更好的结果。两个班次并不复杂,除了霓虹灯中的 AFAIK 只有一个班次单元,但编译器在生成的代码中传播班次。
-
你能不能把它作为答案,我认为它应该比我的代码更好
标签: c++ arm intrinsics neon