有效计算arm neon

时间:2018-04-19 19:23:42

标签: c++ arm intrinsics neon

如何在8个字节,8个短路或8个整数的数组中找到max元素? 我可能只需要max元素的位置,max元素的值或两者的位置。

For example

unsigned FindMax8(const uint32_t src[8]) // returns position of max element
{
    unsigned ret = 0;
    for (unsigned i=0; i<8; ++i)
    {
        if (src[i] > src[ret])
            ret = i;
    }
    return ret;
}

-O2 clang展开循环,但它不使用氖,这应该提供良好的性能提升(因为它消除了许多数据相关的分支?)

对于8个字节和8个短路方法应该更简单,因为整个阵列可以加载到单个q寄存器中。对于arm64来说,使用vmaxv_u16会更加简单,但是如何在32位霓虹灯中提高效率呢?

正如Marc在评论中所指出的,当函数更改为返回最大值GCC auto vectorizer generates时,对于neon64:

ldr q0, [x0, 16]
ld1r {v2.4s}, [x0]
ldr q1, [x0]
umax v0.4s, v0.4s, v2.4s
umax v0.4s, v0.4s, v1.4s
umaxv s0, v0.4s
umov w0, v0.s[0]

我有一个函数执行相当复杂的数学运算,在计算结束时我得到uint32x4_t res结果,我需要的是得到max元素的索引。这个单件是代码中最慢的部分,到目前为止比其余数学函数的其余部分慢。

我尝试了三种不同的方法(根据探查器从最慢到最快):

  • 使用霓虹灯进行全面计算,最后一次32位结果从霓虹灯传输到手臂。

  • vst1q_u32(src,res)然后使用常规C代码查找max元素的索引。

  • 使用vget_lane_u64两次对四个32位arm寄存器进行vmov,然后使用一些位移来计算max元素的索引。

Here's fastest version that I was able to get

unsigned compute(unsigned short *input)
{
    uint32x4_t result = vld1q_u32((uint32_t*)(input));
    // some computations...
    // ... and at the end I end up with res01 and res23
    // and I need to get index of max element from them:
    uint32x2_t res01 = vget_low_u32(result);
    uint32x2_t res23 = vget_high_u32(result);

    // real code below:
    uint64_t xres01 = vget_lane_u64(vreinterpret_u64_u32(res01), 0);
    uint64_t xres23 = vget_lane_u64(vreinterpret_u64_u32(res23), 0);
    unsigned ret = 0;
    uint32_t xmax0 = (uint32_t)(xres01 & 0xffffffff);
    uint32_t xmax1 = (uint32_t)(xres01 >> 32);
    uint32_t xmax2 = (uint32_t)(xres23 & 0xffffffff);
    uint32_t xmax3 = (uint32_t)(xres23 >> 32);
    if (xmax1 > xmax0)
    {
        xmax0 = xmax1;
        ret = 1;
    }
    if (xmax2 > xmax0)
    {
        xmax0 = xmax2;
        ret = 2;
    }
    if (xmax3 > xmax0)
        ret = 3;
    return ret;
}

使用全霓虹计算的版本执行此操作:

  • 使用vmax / vpmax查找最大元素
  • 将u32x4_t设置为最大元素
  • 使用vceq将max elements设置为0xffffffff
  • 使用{1u<<31, 1u<<30, 1u<<29, 1u<<28 }
  • 加载u32x4_t掩码
  • 使用面具做vand
  • 成对添加或vorr将所有4个值折叠为单个值。
  • 使用vclz将all设置为max元素的索引

也许在其他地方发帖,请参阅actual code我正在尝试优化。 My optimized version只需要改进最后一块。不知何故,探查器显示在我计算最大索引的最后一行中花费了80%的时间。有任何想法吗?将简单的c-loop更改为reg对可以将整个功能提高20-30%。请注意,根据分析器,两个vst1_u32是函数大部分时间都在使用的函数。

我可以尝试其他什么方法?

更新 似乎功能结束时的减速与代码无关。我不确定原因,但是当I tried to run different versions of the function取决于我打电话给他们的顺序时,我的时间会改变3-4倍。此外,通过不同的测试,如果在功能结束时没有失速,那么完整的霓虹灯版本似乎是最快的,并且我不确定为什么会发生这种失速。出于这个原因I created a new question找出原因。

0 个答案:

没有答案