我有一个任务-通过以大列为主的矩阵(10000行,400列)来乘以大行向量(10000个元素)。我对ARM NEON感到好奇,因此决定选择ARM NEON,并且想了解更多有关它的信息。

这是我写的向量矩阵乘法的工作示例:

//float* vec_ptr - a pointer to vector
//float* mat_ptr - a pointer to matrix
//float* out_ptr - a pointer to output vector
//int matCols - matrix columns
//int vecRows - vector rows, the same as matrix

for (int i = 0, max_i = matCols; i < max_i; i++) {
    for (int j = 0, max_j = vecRows - 3; j < max_j; j+=4, mat_ptr+=4, vec_ptr+=4) {
        float32x4_t mat_val = vld1q_f32(mat_ptr);    //get 4 elements from matrix
        float32x4_t vec_val = vld1q_f32(vec_ptr);    //get 4 elements from vector

        float32x4_t out_val = vmulq_f32(mat_val, vec_val);  //multiply vectors
        float32_t total_sum = vaddvq_f32(out_val);          //sum elements of vector together
        out_ptr[i] += total_sum;
    }

    vec_ptr = &myVec[0];   //switch ptr back again to zero element
}


问题是,计算的时间很长-如果我的目标是1毫秒或什至更少,则iPhone 7+上的计算时间为30毫秒。因为我启动了乘法迭代400 *(10000/4)= 1000000次,所以当前执行时间是可以理解的。

另外,我尝试处理8个元素而不是4个元素。这似乎有所帮助,但是数字离我的目标还有很远。

我知道,由于我是ARM NEON的新手,所以我可能会犯一些可怕的错误。如果有人可以给我一些如何优化代码的提示,我将感到非常高兴。

另外-是否值得通过ARM NEON进行大向量矩阵乘法?这项技术是否适合于此目的?

最佳答案

您的代码完全有缺陷:假设matColsvecRows均为4,它会迭代16次。那么,SIMD的意义何在?

而主要的性能问题在于float32_t total_sum = vaddvq_f32(out_val);
绝对不要在循环内将向量转换为标量,因为它会导致流水线危害,每次耗时约15个周期。

解决方案:

    float32x4x4_t myMat;
    float32x2_t myVecLow, myVecHigh;

    myVecLow = vld1_f32(&pVec[0]);
    myVecHigh = vld1_f32(&pVec[2]);
    myMat = vld4q_f32(pMat);

    myMat.val[0] = vmulq_lane_f32(myMat.val[0], myVecLow, 0);
    myMat.val[0] = vmlaq_lane_f32(myMat.val[0], myMat.val[1], myVecLow, 1);
    myMat.val[0] = vmlaq_lane_f32(myMat.val[0], myMat.val[2], myVecHigh, 0);
    myMat.val[0] = vmlaq_lane_f32(myMat.val[0], myMat.val[3], myVecHigh, 1);

    vst1q_f32(pDst, myMat.val[0]);



一次计算所有四行
通过vld4快速进行矩阵转置(旋转)
进行矢量标量乘法累加而不是矢量-矢量乘法和水平相加会导致流水线危害。


您是在问SIMD是否适合矩阵运算?一个简单的“是”将是一种巨大的轻描淡写。您甚至不需要为此循环。

关于ios - 通过ARM NEON进行矢量矩阵乘法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57791156/

10-13 02:22