ARM中的NEON实现

时间:2018-03-13 05:11:44

标签: arm simd neon

我是NEON的初学者,想要优化以下代码,但是当它编译并产生所需的相同输出时,我看不到任何改进。 AFAIK NEON有助于对连续的数据块进行操作,所以我希望在执行时间和周期方面有所改进。我做错了什么?

我正在使用-bu级优化的Ubuntu 12.04上的gcc

正常的c实现

git push origin master

霓虹灯表格

for(i= 0;i<9215;i++)
            {
                Z[i] = (L[i]>0)?0:1;    
            }

1 个答案:

答案 0 :(得分:3)

问题:

  • 您在循环内使用效率非常低的算法
  • 您的日常工作遭受重型管道联锁,指导说明
void isNonNatural(int32_t * pDst, int32_t *pSrc, int n)
{
    int32x4_t vec;
    const int32x4_t one = vdupq_n_s32(1);
    int32_t a;

    unsigned int i;

    if (n >= 4)
    {
        n -= 4;
        while (1) {
            do {
                n -= 4;
                vec = vld1q_s32(pSrc++);
                vec = vqsubq_s32(vec, one);
                vec = (int32x4_t) vshrq_n_u32((uint32x4_t) vec, 31);
                vst1q_s32(pDst++, vec);
            } while (n >= 0);

            if (n <= -4) return;

            // dealing with residuals

            pSrc += n;  // rewind pointers
            pDst += n;
        } // iterate for one last time
    }

    for (i = 0; i < n; ++i) {
        a = *pSrc++;
        if (a > 0) a = 0; else a = 1;
        *pDst++ = a;
    }
}

上面的这个函数应该比你的实现快一些。

  • 进行1的饱和减法,使得0变为-1而0x80000000保持为0x80000000
  • 元素移位31位,只留下符号位。
  • 我可以使用0xffffffff而不是1,您可以省略类型转换并使用vshrq_n_s32代替。但它不会更快。
  • 注意剩余管理。

编程NEON就像驾驶一辆大卡车一样。你不应该像小型车那样驾驶它。

虽然NEON可以同时计算多个数据,主要是在一个周期内,但它具有更高的指令延迟,通常为3~4个周期。换句话说,每个指令都必须等待前一个指令才能在上面的实现中返回结果。

实际上,避免这种情况的唯一方法是展开,这是一个很深的。

void isNonNatural_unroll(int32_t * pDst, int32_t *pSrc, int n)
{
    int32x4_t vec1, vec2, vec3, vec4;
    const int32x4_t one = vdupq_n_s32(1);
    int32_t a;

    unsigned int i;

    if (n >= 16)
    {
        n -= 16;
        while (1) {
            do {
                n -= 16;
                vec1 = vld1q_s32(pSrc++);
                vec2 = vld1q_s32(pSrc++);
                vec3 = vld1q_s32(pSrc++);
                vec4 = vld1q_s32(pSrc++);
                vec1 = vqsubq_s32(vec1, one);
                vec2 = vqsubq_s32(vec2, one);
                vec3 = vqsubq_s32(vec3, one);
                vec4 = vqsubq_s32(vec4, one);
                vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
                vec2 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec2, 31);
                vec3 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec3, 31);
                vec4 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec4, 31);
                vst1q_s32(pDst++, vec1);
                vst1q_s32(pDst++, vec2);
                vst1q_s32(pDst++, vec3);
                vst1q_s32(pDst++, vec4);
            } while (n >= 0);

            if (n <= -16) return;

            // dealing with residuals

            pSrc += n;  // rewind pointers
            pDst += n;
        } // iterate for one last time
    }

    if (n & 8)
    {
        vec1 = vld1q_s32(pSrc++);
        vec2 = vld1q_s32(pSrc++);
        vec1 = vqsubq_s32(vec1, one);
        vec2 = vqsubq_s32(vec2, one);
        vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
        vec2 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec2, 31);
        vst1q_s32(pDst++, vec1);
        vst1q_s32(pDst++, vec2);
    }

    if (n & 4)
    {
        vec1 = vld1q_s32(pSrc++);
        vec1 = vqsubq_s32(vec1, one);
        vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
        vst1q_s32(pDst++, vec1);
    }

    n &= 3;

    for (i = 0; i < n; ++i) {
        a = *pSrc++;
        if (a > 0) a = 0; else a = 1;
        *pDst++ = a;
    }
}

现在这个应该比以前的快得多,因为几乎所有的延迟都被隐藏(超过四倍的速度),只要可怜的编译器不会搞砸它。