ARM中的乘法和存储的霓虹优化

时间:2018-03-15 17:50:36

标签: gcc arm simd intrinsics neon

使用ARM Cortex A15主板我尝试使用NEON内在函数优化完美的C代码。

编译器:ubuntu 12.04上的gcc 4.7

标志:-g -O3 -mcpu = cortex-a15 -mfpu = neon-vfpv4 -ftree-vectorize -DDRA7XX_ARM -DARM_PROC -DSL -funroll-loops -ftree-loop-ivcanon -mfloat-abi = hard

我想做以下功能,它只是一个简单的load-> multiply->存储。

这里有一些参数:    * input是指向大小为40680的数组的指针,在完成循环后,指针应保留当前位置,并通过输入指针对下一个输入流执行相同操作。

            float32_t A=0.7;
            float32_t *ptr_op=(float*)output[9216];
            float32x2_t reg1;

             for(i= 0;i< 4608;i+=4){        
                /*output[(2*i)] = A*(*input); // C version
                input++;                 
                output[(2*i)+1] = A*(*input);
                input++;*/

                reg1=vld1q_f32(input++);    //Neon version              
                R_N=vmulq_n_f32(reg1,A);
                vst1q_f32(ptr_op++,R_N);
            } 

我想知道我在这个循环中犯了什么错误,因为它看起来非常简单。

这是我的程序集实现。我正朝着正确的方向前进吗?

__asm__ __volatile__(
              "\t mov r4, #0\n"
              "\t vdup.32 d1,%3\n"
              "Lloop2:\n"
              "\t cmp r4, %2\n"
              "\t bge Lend2\n"
              "\t vld1.32  d0, [%0]!\n"             
              "\t vmul.f32 d0, d0, d1\n"
              "\t vst1.32 d0, [%1]!\n"
              "\t add r4, r4, #2\n"
              "\t b Lloop2\n"
              "Lend2:\n"
              : "=r"(input), "=r"(ptr_op), "=r"(length), "=r"(A)
              : "0"(input), "1"(ptr_op), "2"(length), "3"(A)
              : "cc", "r4", "d1", "d0");

1 个答案:

答案 0 :(得分:0)

嗯,你的代码首先编译了吗?我不知道你可以用浮点标量乘以一个向量。可能编译器确实为你转换了。

无论如何,你必须明白大多数NEON指令都有很长的延迟。除非你正确地隐藏它们,否则你的代码不会比标准的C版本更快,如果不是更慢的话。

vld1q..... // 1 cycle
// 4 cycles latency + potential cache miss penalty
vmulq..... // 2 cycles
// 6 cycles latency
vst1q..... // 1 cycle
// 2 cycles loop overhead

上面的例子大致显示了每次迭代所需的周期。

正如您所看到的,它至少有18个周期/迭代,其中只有4个周期用于实际计算,而14个周期则无意义地浪费。

它被称为RAW dependency(写后读)

隐藏这些延迟的最简单且实际上唯一的方法是循环展开:深度展开。

每次迭代展开四个向量通常就足够了,如果你不介意代码长度,那么八个就更好了。

void vecMul(float * pDst, float * pSrc, float coeff, int length)
{
    const float32x4_t scal = vmovq_n_f32(coeff);
    float32x4x4_t veca, vecb;

    length -= 32;

    if (length >= 0)
    {
        while (1)
        {
            do
            {
                length -= 32;
                veca = vld1q_f32_x4(pSrc++);
                vecb = vld1q_f32_x4(pSrc++);

                veca.val[0] = vmulq_f32(veca.val[0], scal);
                veca.val[1] = vmulq_f32(veca.val[1], scal);
                veca.val[2] = vmulq_f32(veca.val[2], scal);
                veca.val[3] = vmulq_f32(veca.val[3], scal);
                vecb.val[0] = vmulq_f32(vecb.val[0], scal);
                vecb.val[1] = vmulq_f32(vecb.val[1], scal);
                vecb.val[2] = vmulq_f32(vecb.val[2], scal);
                vecb.val[3] = vmulq_f32(vecb.val[3], scal);

                vst1q_f32_x4(pDst++, veca);
                vst1q_f32_x4(pDst++, vecb);
            } while (length >= 0);

            if (length <= -32) return;

            pSrc += length;
            pDst += length;
        }
    }

///////////////////////////////////////////////////////////////

    if (length & 16)
    {
        veca = vld1q_f32_x4(pSrc++);
    }

    if (length & 8)
    {
        vecb.val[0] = vld1q_f32(pSrc++);
        vecb.val[1] = vld1q_f32(pSrc++);
    }

    if (length & 4)
    {
        vecb.val[2] = vld1q_f32(pSrc++);
    }

    if (length & 2)
    {
        vld1q_lane_f32(pSrc++, vecb.val[3], 0);
        vld1q_lane_f32(pSrc++, vecb.val[3], 1);
    }

    if (length & 1)
    {
        vld1q_lane_f32(pSrc, vecb.val[3], 2);
    }

    veca.val[0] = vmulq_f32(veca.val[0], scal);
    veca.val[1] = vmulq_f32(veca.val[1], scal);
    veca.val[2] = vmulq_f32(veca.val[2], scal);
    veca.val[3] = vmulq_f32(veca.val[3], scal);
    vecb.val[0] = vmulq_f32(vecb.val[0], scal);
    vecb.val[1] = vmulq_f32(vecb.val[1], scal);
    vecb.val[2] = vmulq_f32(vecb.val[2], scal);
    vecb.val[3] = vmulq_f32(vecb.val[3], scal);

    if (length & 16)
    {
        vst1q_f32_x4(pDst++, veca);
    }

    if (length & 8)
    {
        vst1q_f32(pDst++, vecb.val[0]);
        vst1q_f32(pDst++, vecb.val[1]);
    }

    if (length & 4)
    {
        vst1q_f32(pDst++, vecb.val[2]);
    }

    if (length & 2)
    {
        vst1q_lane_f32(pDst++, vecb.val[3], 0);
        vst1q_lane_f32(pDst++, vecb.val[3], 1);

    }

    if (length & 1)
    {
        vst1q_lane_f32(pDst, vecb.val[3], 2);
    }
}

现在我们正在处理8个独立向量,因此延迟完全被隐藏,潜在的高速缓存未命中损失以及扁平循环开销正在逐渐减少。

相关问题