如何使用SIMD指令截断值

时间:2014-03-09 10:19:29

标签: simd neon

val = ( val < 0 ) ? 0 : val;

我想要上述指示。 (即)如果val小于dan 0那么将具有值'0'并且如果val小于0,那么'val'将是结果。 是否有任何一组霓虹灯指令将执行上述操作?

2 个答案:

答案 0 :(得分:4)

有可能。使用NEON非常简单,因为它有最小和最大指令。

以下是使用float数据类型的示例。

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); 
}

答案 1 :(得分:0)

假设您正在处理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就是0x000000000000。

简单明了。

编辑:

人们似乎对上面代码中的位操作感到困惑。以下是解释:

int MinusIsZero(int n)
{
  if (n < 0) n = 0;
  return n;
}

正如您所看到的,这是一个非常简单的功能,可以满足OP的需求。

然而,由于其矢量性质,这种简单的“if”语句对SIMD来说是一个真正的痛苦。

幸运的是,在没有'if'的情况下,ALU指令非常好。

int MinusIsZero(n)
{
  int mask;
  mask = (n>>31);
  n &= ~mask;
  return n;
}

首先要做的事情是:如果右移有符号的int32 31位,结果只能是0x00000000(如果是正数)或0xffffffff(如果是负数)。

如果n为正,n&amp; ~0x00000000将导致n。

如果n为负数,则n&amp; ~0xffffffff将导致0。

OP想要的是什么。

到目前为止,对于像NEON这样的SIMD单元最有效的方法(ALU指令是最快的),即使在整数核心上,它也是非常理想的方法,因为它不会破坏CPSR。

不必要地破坏CPSR可能会严重削弱管道和乱序执行能力,具体取决于例行程序的其他部分。