如何使用 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 指令截断值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 arm neon 指令右移值

使用 SIMD 指令去交错音频通道

您如何将“while”迭代器转换为 simd 指令?

使用 Mono.Simd SSE 指令进行流控制

使用 SIMD,我如何有条件地仅移动 alpha 通道值为 255 的像素?

深入浅出计算机组成原理:SIMD:如何加速矩阵乘法?(第27讲)