利用SMID指令集对矩阵乘法进行进一步优化

利用AVX-512编程以及循环展开进一步优化

利用AVX-512指令集可同时对512bit数据进行处理,int类型为32bit,因此理论上最多可以将运行速度提升16倍
未优化

DWORD ThreadProc(LPVOID IpParam)//线程函数,用于计算矩阵乘法
{
    MYDATA* pmd = (MYDATA*)IpParam;
    int* A = pmd->A, * B = pmd->B, * C = pmd->C;
    int begin = pmd->begin, end = pmd->end;
    for (int index = begin; index < end; index++)
    {
        int i = index / N, j = index % N;
        C[i * N + j] = 0;
        for (int n = 0; n < N; n++)
        {
            C[i * N + j] += A[i * N + n] * B[j * N + n];
        }
    }
    return 0;
}

运行结果:

矩阵规模1线程2线程4线程8线程16线程32线程64线程100线程1000线程
1024*10242.692s1.299s0.716s0.441s0.339s0.365s0.362s0.01s0.022s
2048*204820.407s10.593s7.006s3.879s2.725s2.787s2.836s0.012s0.017s
4096*4096161.18s94.54s52.838s30.981s22.328ss22.45s22.676s0.01s0.017s

矩阵规模
1024

2048

4096

优化后

DWORD ThreadProc(LPVOID IpParam)//线程函数,用于计算矩阵乘法
{
    MYDATA* pmd = (MYDATA*)IpParam;
    int* A = pmd->A, * B = pmd->B, * C = pmd->C;
    int begin = pmd->begin, end = pmd->end;
    for (int index = begin; index < end; index += 1)
    {
        int i = index / N, j = index % N;
        __m512i res_vec = _mm512_setzero_si512();
        for (int n = 0; n < N; n += 16)//循环展开
        {
            __m512i m1_vec = _mm512_loadu_si512((__m512i*) & A[i * N + n]);
            __m512i m2_vec = _mm512_loadu_si512((__m512i*) & B[j * N + n]);
            res_vec = _mm512_add_epi32(res_vec, _mm512_mullo_epi32(m1_vec, m2_vec));
        }
        int* p1 = (int*)&res_vec;
        C[i * N + j] += (p1[0] + p1[1] + p1[2] + p1[3] + p1[4] + p1[5] + p1[6] + p1[7]+p1[8] + p1[9] + p1[10] + p1[11] + p1[12] + p1[13] + p1[14] + p1[15]);
    }
    return 0;
}

运行结果

矩阵规模1线程2线程4线程8线程16线程32线程64线程65线程100线程1000线程
1024*10240.327s0.18s0.108s0.088s0.063s0.074s0.085s0.013s0.028s0.409s
2048*20483.818s1.602s0.882s0.543s0.358s0.352s0.368s0.01s0.009s0.079s
4096*409623.983s13.813s7.67s4.89s3.239s3.855s3.929s0.002s0.003s0.03s

矩阵规模
1024

2048

4096

对比发现,采用AVX-512指令集以及循环展开优化后,相比与之前,加速了近10倍,但是当线程数超过64之后,运行速度发生了质变,这时检测矩阵发现并未全部计算完毕,猜测可能为线程数太多,超出库函数所能运行的极限,因此线程被提前销毁,导致未能计算完毕。

发现当线程数为16时运行速度最快,查看cpu规格,cpu线程数为16,因此猜测当程序线程数接近或等于cpu线程数时,运行速度最快。当线程数少时,cpu未全部占用,当线程数多时,操作系统调度可能浪费部分时间。

完整代码

#include <windows.h>
#include <iostream>
#include <ctime>
#include <xmmintrin.h>
#include "immintrin.h"
#define N   2048
#define MAX_THREADS 10
using namespace std;
struct MYDATA //用于传递多个参数给线程函数
{
    int begin, end;
    int* A, * B, * C;
};
DWORD ThreadProc(LPVOID IpParam)//线程函数,用于计算矩阵乘法
{
    MYDATA* pmd = (MYDATA*)IpParam;
    int* A = pmd->A, * B = pmd->B, * C = pmd->C;
    int begin = pmd->begin, end = pmd->end;
    for (int index = begin; index < end; index += 1)
    {
        int i = index / N, j = index % N;
        __m512i res_vec = _mm512_setzero_si512();
        for (int n = 0; n < N; n += 16)
        {
            __m512i m1_vec = _mm512_loadu_si512((__m512i*) & A[i * N + n]);
            __m512i m2_vec = _mm512_loadu_si512((__m512i*) & B[j * N + n]);
            res_vec = _mm512_add_epi32(res_vec, _mm512_mullo_epi32(m1_vec, m2_vec));
        }
        int* p1 = (int*)&res_vec;
        C[i * N + j] += (p1[0] + p1[1] + p1[2] + p1[3] + p1[4] + p1[5] + p1[6] + p1[7]+p1[8] + p1[9] + p1[10] + p1[11] + p1[12] + p1[13] + p1[14] + p1[15]);
    }
    return 0;
}
int* Mul(int A[], int B[])
{
    int* M = new int[N * N];
    for (int index = 0; index < N * N; index++)
    {
        int i = index / N, j = index % N;
        M[i * N + j] = 0;
        for (int n = 0; n < N; n++)
        {
            M[i * N + j] += A[i * N + n] * B[j * N + n];
        }
    }
    return M;
}

int* A=new int[N * N];
int* B = new int[N * N];
int C[N * N];
int* revB = new int[N * N];
double rate[MAX_THREADS];
int threads[MAX_THREADS] = {1,2,4,8,16,32,64,65,100,1000};

int main()
{
    for (int i = 0; i < N * N; i++)
    {
        A[i] = rand() % 2;
        B[i] = rand() % 2;
    }
    for (int index = 0; index < N * N; index++)
    {
        int i = index / N, j = index % N;
        revB[i * N + j] = B[j * N + i];
    }
    for (int i = 0; i < MAX_THREADS; i++)
    {
        int m = threads[i];
        clock_t start, end;
        start = clock();
        HANDLE hThread[N];//初始化临界区
        static MYDATA mydt[N];
        int temp = (N * N) / m;
        for (int i = 0; i < m; i++)
        {
            mydt[i].A = A, mydt[i].B = revB, mydt[i].C = C;
            mydt[i].begin = i * temp, mydt[i].end = i * temp + temp;
            if (i == m - 1)//最后一个线程
            {
                mydt[i].end = N * N;
            }
            hThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, &mydt[i], 0, NULL);//创建线程
        }
        WaitForMultipleObjects(m, hThread, TRUE, INFINITE);
        end = clock();
        cout << m << "个线程时运行时间为" << (double)(end - start) / CLOCKS_PER_SEC << "s" << endl;
    }
}

python

#单线程实现N*N矩阵乘法
import time
def mul(A=[],B=[]):
    n=len(A)
    C=[[0]*n for row in range(n)]
    for m in range(n):
        for i in range(n):
            for j in range(n):
                C[m][i]+=A[m][j]*B[j][i]
    return C;
A= [[1] * 1024 for _ in range(1024)]
B= [[1] * 1024 for _ in range(1024)]
start=time.time()
C=mul(A,B)
end=time.time()
print("运行时间:",end-start)
#多线程实现N*N矩阵乘法

运行时间

矩阵规模1024*10242048*20484096*4096
运行时间96.096s763.159s ∞ \infty

商业库

矩阵规模1024*10242048*20484096*4096
运行时间0.0339068s0.265126s2.09942s

最高加速比(对比python)

矩阵规模1024*10242048*20484096*4096
加速比1525.3342168.065 ∞ \infty

通过多线程以及AVX-512指令集合对程序优化后,对比python基准程序最终可实现上千倍优化。

与官方库差距原因:

算法复杂度:矩阵乘法算法的实现可能存在复杂度较高的部分。商业库可能使用了更高效的算法或优化技术来降低计算复杂度,从而提高性能。

并行度:商业库可能使用更高级的并行化技术来充分利用多核处理器。你的优化代码使用了线程来并行执行矩阵乘法,但商业库可能有更细粒度的并行化,利用了更多的硬件资源。

内存访问模式:矩阵乘法的性能受到内存访问的影响。商业库可能通过优化内存访问模式,如使用缓存友好的布局或预取技术,减少了内存访问延迟。在你的优化代码中,对于大型矩阵,内存访问可能不够高效,导致性能下降。

编译器优化:商业库可能使用专门优化的编译器,针对特定硬件和算法进行了深度优化。编译器优化级别、指令选择和代码生成等因素都可以对性能产生影响。

SIMD 和指令集:商业库可能使用了更高级的 SIMD(单指令多数据)指令集来进行向量化操作,从而提高计算性能。你的优化代码中使用的是 AVX-512 SIMD 指令集,但某些商业库可能支持更高级别的 SIMD 指令集,例如 AVX2 或 AVX-512 的高级优化。

性能下降。

编译器优化:商业库可能使用专门优化的编译器,针对特定硬件和算法进行了深度优化。编译器优化级别、指令选择和代码生成等因素都可以对性能产生影响。

SIMD 和指令集:商业库可能使用了更高级的 SIMD(单指令多数据)指令集来进行向量化操作,从而提高计算性能。你的优化代码中使用的是 AVX-512 SIMD 指令集,但某些商业库可能支持更高级别的 SIMD 指令集,例如 AVX2 或 AVX-512 的高级优化。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想去卖防晒油的象龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值