用OpenMP对矩阵乘法运算进行优化

用OpenMP对矩阵乘法运算进行优化

数学算式:
在这里插入图片描述
在这里插入图片描述
算法流程伪代码:
在这里插入图片描述

在进行优化之前,先来讨论一下
用parallel for处理循环易遇到问题:
参考文章:
openmp中parallel for处理双重循环遇到问题的分析和解决方案
作为初学者使用parallel for的双重循环,代码结果一直计算不对,但是一直没找到原因。后来发现原因挺简单的,就是在双重循环的时候,假如第一层是遍历 i,第二层是遍历 j,这个 j 如果是omp_set_num_threads()之前声明的,就说明 j 被多个线程共用,那么可能这个线程正在使用 j,然后被另一个线程更改了
例如:

#include <omp.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
    int i, j;
    printf("initila i address is %p, initial j address is %p\n", &i, &j);
    
    omp_set_num_threads(2);

    #pragma omp parallel for
    for (i = 0; i < 4; ++i)
    {
        for (j = 0; j < 1; ++j)
        {
            printf("thread is %d, i=%d, i address is %p, j=%d, j address is %p\n", 
                    omp_get_thread_num(), i, &i, j, &j);
        }
    }

    return 0;
}
initila i address is 000000000070FE0C, initial j address is 000000000070FE08
thread is 0, i=0, i address is 000000000070FD4C, j=0, j address is 000000000070FE08
thread is 0, i=1, i address is 000000000070FD4C, j=0, j address is 000000000070FE08
thread is 1, i=2, i address is 000000000141FDEC, j=0, j address is 000000000070FE08
thread is 1, i=3, i address is 000000000141FDEC, j=0, j address is 000000000070FE08

可以看到,一共两个线程,i 和 j 都是在开辟线程之前定义,但是openmp parallel for会为每个线程新创建一个 i,但是并不会新建一个 j,j 还是最开始创建的那个。这就导致双重循环中,第二重循环的 j 产生数据冲突

解决方案:
使用parallel for,只会把最外层的循环分给不同的线程,同一个外层循环的内层的循环只能由一个线程执行,要想实现同一个外层循环的内层循环也被多个线程执行,我们可以写循环的时候,内层循环写成:for (int j = 0; j < xxx; ++j),就可以为每个线程创建不同的 j 了,避免数据冲突。或者我们可以把#pragma omp parallel for(private j),强制让每个线程新创建 j

——————————————————
这里采用前者进行编写。

#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
#define N 300
int main()
{
	//随机生成300阶矩阵
	int a[N][N],b[N][N],c[N][N]={0};
	double t0,t1;
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<N;j++)
		{
			a[i][j]=rand()%100;
			b[i][j]=rand()%100;
		}
	}
	
    //初始矩阵乘法运算
	t0=omp_get_wtime();
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
			for(int k=0;k<N;k++)
				c[i][j]+=a[i][k]*b[k][j];
	t1=omp_get_wtime();
	printf("初始矩阵乘法运行耗时:%f ms\n",(t1-t0)*1000); 
	
	//优化矩阵乘法运算 
	t0=omp_get_wtime();
	#pragma omp parallel for
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
            for(int k=0;k<N;k++)
				c[i][j]+=a[i][k]*b[k][j];
	t1=omp_get_wtime();
	printf("优化矩阵乘法(默认调度)运行耗时:%f ms\n",(t1-t0)*1000); 
	
	//优化矩阵乘法运算 (静态调度) 
	t0=omp_get_wtime();
	#pragma omp parallel for schedule(static)
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
            for(int k=0;k<N;k++)
				c[i][j]+=a[i][k]*b[k][j];
	t1=omp_get_wtime();
	printf("优化矩阵乘法(静态调度)运行耗时:%f ms\n",(t1-t0)*1000); 
	
	// 优化矩阵乘法运算 (动态调度) 
	t0=omp_get_wtime();
	#pragma omp parallel for schedule(dynamic)
	for(int i=0;i<N;i++)
		for(int j=0;j<N;j++)
            for(int k=0;k<N;k++)
				c[i][j]+=a[i][k]*b[k][j];
	t1=omp_get_wtime();
	printf("优化矩阵乘法(动态调度)运行耗时:%f ms\n",(t1-t0)*1000); 
	return 0;
}

结果:
在这里插入图片描述
这里可以看到static和dynamic差距并不大,应该是矩阵乘法任务本身for循环中的计算量大致相同的原因

对于稀疏矩阵,可以采用CSR法对数据进行储存

算法方面呢,也可以采用分块矩阵,提高运算效率:矩阵乘法的分块优化
分块的矩阵乘法是为了利用局部性提高cache(高速缓存)利用率

t0=omp_get_wtime();
	const int B=10;
	for(int i1=0;i1<N;i1+=B)
	{
		for(int j1=0;j1<N;j1+=B)
		{
			for(int k1=0;k1<N;k1+=B)
			{
				for(int i2=i1;i2<i1+B;i2++)
				{
					for(int j2=j2;j2<j1+B;j2++)
					{
						for(int k2=k1;k2<k1+B;k2++)
						c[i2][j2]+=a[i2][k2]*b[k2][j2]; 
					}
						
				}
					
			}
				
		}
			
	}
	t1=omp_get_wtime();
	printf("分块矩阵乘法运行耗时:%f ms\n",(t1-t0)*1000); 

将之与普通矩阵乘法进行对比,结果如下

在这里插入图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用OpenMP实现矩阵乘法的示例代码: ```c++ #include <omp.h> #include <stdio.h> #include <stdlib.h> #define NRA 1000 /* number of rows in matrix A */ #define NCA 1000 /* number of columns in matrix A */ #define NCB 1000 /* number of columns in matrix B */ int main (int argc, char *argv[]) { int tid, nthreads, i, j, k; double a[NRA][NCA], /* matrix A to be multiplied */ b[NCA][NCB], /* matrix B to be multiplied */ c[NRA][NCB]; /* result matrix C */ /* Initialize matrices */ for (i=0; i<NRA; i++) for (j=0; j<NCA; j++) a[i][j]= i+j; for (i=0; i<NCA; i++) for (j=0; j<NCB; j++) b[i][j]= i*j; for (i=0; i<NRA; i++) for (j=0; j<NCB; j++) c[i][j]= 0; /* Perform matrix multiplication with OpenMP */ #pragma omp parallel shared(a,b,c) private(tid,i,j,k) { tid = omp_get_thread_num(); if (tid == 0) { nthreads = omp_get_num_threads(); printf("Starting matrix multiplication with %d threads\n",nthreads); } #pragma omp for schedule(static) for (i=0; i<NRA; i++) { printf("Thread %d starting row %d\n",tid,i); for(j=0; j<NCB; j++) for (k=0; k<NCA; k++) c[i][j] += a[i][k] * b[k][j]; } } /* Print results */ printf("******************************************************\n"); printf("Result Matrix:\n"); for (i=0; i<NRA; i++) { for (j=0; j<NCB; j++) printf("%6.2f ", c[i][j]); printf("\n"); } printf("******************************************************\n"); } ``` 这个示例代码中,我们使用了OpenMP的并行化技术来加速矩阵乘法的计算。在主函数中,我们首先定义了三个矩阵a、b和c,然后对矩阵a和b进行了初始化。接着,我们使用OpenMP的#pragma omp parallel指令来创建一个并行区域,其中shared(a,b,c)表示a、b和c是共享变量,private(tid,i,j,k)表示tid、i、j和k是私有变量。在并行区域中,我们使用#pragma omp for指令来并行化矩阵乘法的计算,其中schedule(static)表示采用静态调度方式。最后,我们输出了计算结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值