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

时间:2017-11-16 01:26:19

标签: arm neon

我想在ARM处理器上处理大量的浮点数, 使用霓虹灯技术一次四个计算它们。对于像加法和乘法这样的操作,一切都很好,但如果我的计算进入IF块,我该怎么办?例如:

i = 9.60000

现在,我执行哪个IF分支?如果正在处理的向量中的某些值大于10而某些值较小,该怎么办?甚至可以像这样对代码进行矢量化吗?

3 个答案:

答案 0 :(得分:2)

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

  1. 通常,您不会根据并行寄存器内容执行IF块逻辑,因为一个值可能需要IF块的一个分支,而同一个寄存器中的不同值可能需要另一个。 “急切执行”意味着首先进行所有可能的计算,然后确定哪些结果实际用于哪个通道。 (请记住,通过仅对寄存器的一个通道进行氖计算,您无法获得任何结果。任何必须完成的计算,都可以完成所有2或4个通道。)

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

  3. double 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.)"
    

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

    AA = vbslq_f64(IF1, VALIFTRUE, VALIFFALSE);  // {21.5, 14.5}
    

    ...并且AA的每个车道都适用于该车道的VALIFTRUE或VALIFFALSE。

    1. 如果急切的执行速度太慢怎么办 - 一个分支中的计算在处理器时间上是非常昂贵的,如果可以的话,你想要避免执行它们?您必须验证任何向量通道的分支条件是否为真,然后使用正确的“if”语句跳过计算。也许其他人可以评论这在实践中的效果如何。

答案 1 :(得分:1)

If-else障碍赛对于几乎所有的CPU来说都是一场噩梦,特别是像NEON这样的矢量机器本身没有任何条件分支。

因此我们应用"急切执行"对这样的问题。

  • 创建布尔蒙版
  • 计算ifelse
  • "对"结果由掩码选择

我认为将下面的aarch32代码转换为内在函数不会有问题。

//aarch32
    vadd.f32    vecElse, vecA, vecTen // vecTen contains 10.0f
    vcgt.f32    vecMask, vecA, vecTen
    vadd.f32    vecA, vecA, vecFive
    vbif        vecA, vecElse, vecMask

//aarch64
    fadd    vecElse.4s, vecA.4s, vecTen.4s
    fcmgt   vecMask.4s, vecA.4s, vecTen.4s
    fadd    vecA.4s, vecA.4s, vecFive.4s
    bif     vecA.16b, vecElse.16b, vecMask.16b

答案 2 :(得分:0)

通常使用SIMD分支逻辑,您可以使用比较掩码,然后相应地选择备用结果。我将为您的示例提供伪代码,您可以根据需要将其转换为内在函数或asm:

v5 = vector(5)              // set up some constant vectors
v10 = vector(10)
vMask = compare_gt(vA, v10) // generate mask for vector compare A > 10
va = add(vA, v10)           // vA = vA + 10 (all elements, unconditionally)
vtemp = and(v5, vMask)      // generate temp vector of 5 and 0 values based on mask
va = sub(vA, vTemp)         // subtract 5 from elements which are <= 10