使用矢量化 C++ 的矩阵乘法

Posted

技术标签:

【中文标题】使用矢量化 C++ 的矩阵乘法【英文标题】:Matrix Multiplication using vectorized c++ 【发布时间】:2019-11-19 22:59:16 【问题描述】:

我正在尝试编写一个 c++ 代码来使用 SIMD 进行矩阵乘法,但结果是错误的 这是我的代码

    void mat_sse(DATA m1[][SIZE], DATA m2[][SIZE], DATA mout[][SIZE])


    DATA prod = 0;

    __m128 X, Y, Z, M, N;

    for(int i=0; i<SIZE; i=i+1)
    Z[0] = Z[1] = Z[2] = Z[3] = 0;
    for(int k=0; k< SIZE; k=k+4)

        for( int j=0; j<SIZE; j=j+4)
            X = _mm_load_ps(&m1[i][k]);
            Y = _mm_load_ps(&m2[k][j]);
            M = _mm_mul_ps(X, Y);
            Z = _mm_add_ps(M, N);
            mout[i][j] += Z[0];
        mout[i][j+1] += Z[1];
        mout[i][j+2] += Z[2];
        mout[i][j+3] += Z[3];
        

    

    

    return ;


大小在哪里 const int SIZE = 40; 你能帮忙吗?

【问题讨论】:

【参考方案1】:

这有很多问题。

for(int k=0; k< SIZE; k=k+4)
    for( int j=0; j<SIZE; j=j+4)

两个循环都前进了 4,因此内部循环的主体一次处理旧标量循环的 16 步。除了没有,它做了“四件事”。

它们不是正确的东西:

X = _mm_load_ps(&m1[i][k]);
Y = _mm_load_ps(&m2[k][j]);
M = _mm_mul_ps(X, Y);

因此,内部循环的每次迭代都从m1 中取出相同的小行向量,从m2 中取出下一个小行向量,然后将它们逐点相乘。那是行不通的。例如,如果我们有两个 4x4 矩阵:(部分显示)

A B C D   X Y Z W
E . . .   S . . .
I . . . × T . . .
M . . .   U . . .

内部循环的迭代将计算 AX、BY、CZ 和 DW。 AX 确实应该在结果中,但真正的矩阵乘法不涉及 BY:m1 的行与m2 组合,所以 BY 等等第二个m1 行中的条目乘以 m2 列中的第一个条目,不会发生。有许多不同的方式来安排计算,但这里实现的方式不是重新安排,它计算了一些错误的产品并跳过了许多必要的产品。

m2 加载一小行很方便,广播m1 加载单个条目。这样,乘积在mout 中有一小行,因此可以累加并写入结果,而无需进一步洗牌。

顺便说一句,你已经完成了最后一部分,

mout[i][j] += Z[0];
mout[i][j+1] += Z[1];
mout[i][j+2] += Z[2];
mout[i][j+3] += Z[3];

.. 但是将它放在循环中是不好的,只有当产品的结果是应该汇总到这些位置的数字时才有意义。这个加载/求和/存储的东西在内部循环中,因为内部循环是j 循环,但这可以通过交换jk 循环来解决:(未测试)

for (int i = 0; i < SIZE; i++) 
    for (int j = 0; j < SIZE; j += 4) 
        __m128 sum = _mm_setzero_ps();
        for (int k = 0; k < SIZE; k++) 
            __m128 entry = _mm_set1_ps(m1[i][k]);
            __m128 row  = _mm_load_ps(&m2[k][j]);
            sum = _mm_add_ps(sum, _mm_mul_ps(entry, row));
        
        _mm_store_ps(&mout[i][j], sum);
    

由于各种原因,该代码仍然很慢:

通过addps 的循环携带依赖比可用吞吐量慢。使用更多独立的累加器。 每个算术运算的负载过多。 对于大中型矩阵,使用缓存阻塞。不过size = 40 的时候不行。

【讨论】:

【参考方案2】:

在这一行:

Z = _mm_add_ps(M, N);

N 未初始化,因此Z 将成为垃圾。

【讨论】:

我已将 Z[0]=Z[1]=Z[2]=Z[3] =0 放在 i-loop 内部的第二个中,但仍然是同样的问题 DATA是什么类型? 这是一种通用类型,我有一个正常的乘法函数,输出没有错误,但这里的问题是代码,而不是数据类型 DATA 只能是float,否则内联函数是错误的类型。

以上是关于使用矢量化 C++ 的矩阵乘法的主要内容,如果未能解决你的问题,请参考以下文章

使用 openmp 并行化矩阵乘法并使用 avx2 进行矢量化

矩阵乘法的自动向量化

任务是使用 p 线程并行化矩阵乘法并使用英特尔 ISPC 编译器进行矢量化

如何进行快速的多维矩阵向量乘法?

当数组是函数参数时,矩阵乘法中的 Gcc 自动向量化奇怪行为

C++ 乘法大矩阵