我正在尝试使用Neon内在函数为ARM A8处理器编写优化的点积,但遇到了一些麻烦。首先,有没有已经实现此功能的库?我的代码似乎可以运行,但是在运行时会导致一些安静的故障-我的最佳猜测是因为与未优化的代码相比,精度略有下降。有没有更好的方法来完成我要做的事情?如有任何帮助或建议,我将不胜感激。提前致谢。

这个特殊的点积是32位浮点数* 32位浮点数复数。

这是未优化的代码:

    double sum_re = 0.0;
    double sum_im = 0.0;
    for(int i=0; i<len; i++, src1++, src2++)
    {
            sum_re += *src1 * src2->re;
            sum_im += *src1 * src2->im;
    }

这是我的优化版本:
    float sum_re = 0.0;
    float sum_im = 0.0;

    float to_sum_re[4] = {0,0,0,0};
    float to_sum_im[4] = {0,0,0,0};

    float32x4_t tmp_sum_re, tmp_sum_im, source1;
    float32x4x2_t source2;
    tmp_sum_re = vld1q_f32(to_sum_re);
    tmp_sum_im = vld1q_f32(to_sum_im);

    int i = 0;

    while (i < (len & ~3)) {
            source1 = vld1q_f32(&src1[i]);
            source2 = vld2q_f32((const float32_t*)&src2[i]);

            tmp_sum_re = vmlaq_f32(tmp_sum_re, source1, source2.val[0]);
            tmp_sum_im = vmlaq_f32(tmp_sum_im, source1, source2.val[1]);

            i += 4;
    }
    if (len & ~3) {
            vst1q_f32(to_sum_re, tmp_sum_re);
            vst1q_f32(to_sum_im, tmp_sum_im);

            sum_re += to_sum_re[0] + to_sum_re[1] + to_sum_re[2] + to_sum_re[3];
            sum_im += to_sum_im[0] + to_sum_im[1] + to_sum_im[2] + to_sum_im[3];
    }

    while (i < len)
    {
            sum_re += src1[i] * src2[i].re;
            sum_im += src1[i] * src2[i].im;
            i++;
    }

最佳答案

如果您使用的是iOS,请使用Accelerate框架中的vDSP_zrdotpr。 (vDSP_zrdotpr返回实 vector 与复数 vector 的点积。还有其他变体,例如用于实数到实数或复数到复数。)

当然会损失精度。您未优化的代码会累加 double 和,而NEON代码会累加单精度和。

即使不进行精度更改,结果也会有所不同,因为以不同顺序执行浮点运算会产生不同的舍入误差。 (对于整数也是如此;如果计算7/3 * 5,则得到10,但5 * 7/3为11。)

有一些算法可以减少误差进行浮点运算。但是,对于生产高性能的点积产品,通常会受到束缚。

一种选择是使用 double NEON指令进行算术运算。当然,这不会像单精度NEON那样快,但是它将比标量(非NEON)代码快。

关于c++ - 点积/w霓虹本征,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11436551/

10-13 01:59