如何使用 SIMD 指令截断值
Posted
技术标签:
【中文标题】如何使用 SIMD 指令截断值【英文标题】:how to truncate value using SIMD instructions 【发布时间】:2014-03-09 10:19:28 【问题描述】:val = ( val < 0 ) ? 0 : val;
我想要上面的说明。 (即)如果 val 小于 dan 0,则值为 '0',如果 val 大于 0,则结果为 'val'。 是否有任何一套霓虹灯指令可以执行上述操作??
【问题讨论】:
【参考方案1】:这是可能的。使用 NEON 非常简单,因为它有最小和最大指令。
这是一个使用浮点数据类型的示例。
float32x2_t clampToZero (float32x2_t value)
// generate a vector containing all zeros:
float32x2_t allZero = vdup_n_f32 (0.0f);
// take the parallel maximum between your value and zero.
return vmax_f32 (allZero, value);
【讨论】:
【参考方案2】:假设您正在处理 16 位签名数据,d0 包含值:
vshr.s16 d1, d0, #15
vbic.16 d0, d0, d1
这样就可以了。
或者,您可以求助于:
vshll.s16 q0, d0, #16
vqshrun.s32 d0, q0, #16
甚至:
vmovl.s16 q0, d0
vqmovun.s32 d0, q0
即使您正在处理浮点数据,您也可以像处理 s32 一样处理它们:
vshr.s32 d1, d0, #31
vbic.32 d0, d0, d1
你知道,MSB 是 float 和 int 的符号位,而 0.0f 只不过是 0x00000000。
简单明了。
编辑:
人们似乎对我上面代码中的位操作感到困惑。这是解释:
int MinusIsZero(int n)
if (n < 0) n = 0;
return n;
如您所见,这是一个非常简单的函数,可以满足 OP 的要求。
但是,由于 SIMD 的向量性质,如此简单的“if”语句对于 SIMD 来说确实很痛苦。
幸运的是,使用没有“if”的 ALU 指令非常可行。
int MinusIsZero(n)
int mask;
mask = (n>>31);
n &= ~mask;
return n;
首先要做的事情:如果将有符号的 int32 右移 31 位,则结果只能是 0x00000000(如果为正)或 0xffffffff(如果为负)。
如果 n 为正数,则 n & ~0x00000000 将导致 n。
如果 n 为负数,n & ~0xffffffff 将导致 0。
正是 OP 想要的。
除了它是目前为止在 NEON 等 SIMD 单元上最有效的方法,(ALU 指令是最快的)即使在整数内核上它也是一种非常理想的方法,因为它不会破坏 CPSR。
根据例程周围的其他部分,不必要地破坏 CPSR 可能会严重削弱管道和乱序执行能力。
【讨论】:
以上是关于如何使用 SIMD 指令截断值的主要内容,如果未能解决你的问题,请参考以下文章