如何使用 ARM Neon 内在函数对 IF 块进行矢量化?

use*_*572 2 arm neon

我想在 ARM 处理器上处理大量浮点数,使用 Neon 技术一次计算四个浮点数。对于加法和乘法等运算来说一切都很好,但是如果我的计算进入 IF 块,我该怎么办?例子:

// In the non-vectorized original code, A is an array of many floating-point
// numbers, which are calculated one at a time.  Now they're packed 
// into a vector and processed four at a time

...calculate A...

if (A > 10.f)
{
    A = A+5.f;
}
else
{
    A = A+10.f;
}
Run Code Online (Sandbox Code Playgroud)

现在,我要执行哪个 IF 分支?如果正在处理的向量中的某些值大于 10 而某些值小于 10,该怎么办?是否有可能像这样矢量化代码?

use*_*572 5

我将通过描述如何在 Neon 内在函数中对其进行编码来添加到目前为止的答案。

  1. 一般来说,您不会基于并行寄存器内容执行 IF 块逻辑,因为一个值可能需要 IF 块的一个分支,而同一寄存器中的不同值可能需要另一个分支。“热切执行”意味着首先进行所有可能的计算,然后决定在哪些通道中实际使用哪些结果。(请记住,仅对寄存器的一个通道进行 Neon 计算不会获得任何好处。任何必须完成的计算都会针对所有 2 或 4 个通道完成。)

  2. 要进行基于 IF 的计算,请使用 Neon 条件内在函数(例如“大于”)来创建位掩码,然后使用“选择”函数根据位掩码填充最终结果

双 aval[2] = {11.5, 9.5};

float64x2_t AA= vld1q_f64(aval);       // an array with two 64-bit double values

float64x2 TEN= vmovq_n_f64(10.f);      // load a constant into a different array
float64x2 FIVE= vmovq_n_f64(5.f);      // load a constant into a different array

// Do both of the computations
float64x2 VALIFTRUE = vaddq_f64(AA, TEN);  // {21.5, 19.5}
float64x2 VALIFFALSE = vaddq_f64(AA, FIVE);  // {16.5, 14.5}


uint64x2_t IF1 = vcgtq_f64 (AA, TEN);  // comparison "(if A > 10.)"
Run Code Online (Sandbox Code Playgroud)

vcgtq_f64 的返回值不是一组双精度数,而是两个 64 位无符号整数。它们实际上是一个位掩码,可由“按位选择”函数(例如 vbslq_f64)使用。IF1 的前 64 位全为 1(大于条件为真),后 64 位全为 0。

AA = vbslq_f64(IF1, VALIFTRUE, VALIFFALSE);  // {21.5, 14.5}
Run Code Online (Sandbox Code Playgroud)

...并且 AA 的每个通道都根据情况填充该通道的 VALIFTRUE 或 VALIFFALSE。

  1. 如果急切执行太慢怎么办——一个分支中的计算在处理器时间上非常昂贵,并且您希望尽可能避免执行它们?您必须验证该分支条件对于任何向量通道都不成立,然后使用适当的“if”语句跳过计算。也许其他人可以评论这在实践中的效果如何。