【问题标题】:Vector Matrix multiplication via ARM NEON通过 ARM NEON 进行向量矩阵乘法
【发布时间】:2019-09-04 15:02:29
【问题描述】:

我有一个任务 - 通过大列主矩阵(10 000 行,400 列)乘以大行向量(10 000 个元素)。我决定选择 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
}

问题在于计算需要很长时间 - 在 iPhone 7+ 上需要 30 毫秒,而我的目标是 1 毫秒,如果可能的话甚至更少。当前执行时间是可以理解的,因为我启动了乘法迭代 400 * (10000 / 4) = 1 000 000 次。

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

我知道我可能会犯一些可怕的错误,因为我是 ARM NEON 的新手。如果有人能给我一些如何优化我的代码的提示,我会很高兴。

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

【问题讨论】:

  • 也许你需要重新考虑算法,或者像下面提到的 @Jake 那样使用着色器(我相信那将是 OpenGL 或者你可以在 iOS 上使用的任何东西)。
  • ARM CMSIS DSP 库中的任何代码会有帮助吗?那里有使用 NEON 的矩阵乘法函数,它们看起来不像你的。至少值得看看他们是如何做到的。
  • 您好,感谢您的提示!我一定会看看的

标签: ios matrix vector arm neon


【解决方案1】:

您的代码完全有缺陷:假设 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 是否适合矩阵运算?一个简单的“是”将是一个巨大的轻描淡写。你甚至不需要循环。

【讨论】:

  • 也许我明白了,但我相信matCols10000vecRows400
  • @Groo 那根本不可行。也许那时你需要 CUDA。我们真的不需要经典的傅立叶变换,因为我们有 FFT,对吗?
  • 您好,感谢您的回复!但是你能解释一下“假设 matCols 和 vecRows 都是 4,它会迭代 16 次。那么 SIMD 的意义何在?”
  • “你甚至不需要循环” - 你的意思是我不需要循环吗?你能详细说明一下吗?
  • @EugeneAlexeev 简短的回答是肯定的,您可以在 NEON 上进行非常大的矩阵向量乘法。然而,问题是,对于任何规模的问题都没有单一的理想实现。制造汽车与制造火车是完全不同的。关于尺寸还有其他因素:它们是 n 的二次幂吗?它们是四的倍数还是八的倍数?请注意,矩阵向量乘法只不过是矩阵高度乘以 vecRows 长度的点积。这是一项相当容易的任务。
猜你喜欢
  • 2017-07-02
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 2011-09-26
  • 2012-09-28
  • 2020-10-29
相关资源
最近更新 更多