在正确的时候使用多线程的探究


前言

今天在使用OpenMP与C++进行多线程编程的初次尝试时,出现了一个与预想相悖的现象:

在对一个for循环使用多线程运行时,随着线程数的增加,程序的运行时间反而在增加

虽然可能老手一听这个说法就知道问题出在哪里,但是对于我这个刚接触多线程编程的菜鸟来说,我花费了许多的时间在思考和解决这个疑惑上,最终找到了问题所在。

因此我在这里总结一下自己的思路。


一、代码分析

首先先展示一下自己的问题代码。

#include<stdio.h>
#include<omp.h>
#include<time.h>
void comput(float* A,float* B,float* C)//两个矩阵相乘传统方法
{
	int x,y;
	for(y=0;y<4;y++)
		for(x=0;x<4;x++)
			C[4*y+x] = A[4*y+0]*B[4*0+x] + A[4*y+1]*B[4*1+x] + A[4*y+2]*B[4*2+x] + A[4*y+3]*B[4*3+x];
}

int main()
{
    double  duration;    
	clock_t s,f;
    int x=0;
	int y=0;
	int n=0;
	int k=0;
	float A[]={1,2,3,4,
		5,6,7,8,
		9,10,11,12,
		13,14,15,16};
	float B[]={0.1f,0.2f,0.3f,0.4f,
		0.5f,0.6f,0.7f,0.8f,
		0.9f,0.10f,0.11f,0.12f,
		0.13f,0.14f,0.15f,0.16f};
	float C[16];
  
	s= clock(); 
	for(n=0;n<1000000;n++) 	
		comput(A,B,C);
   	f=clock();
	duration = (double)(f - s)/CLOCKS_PER_SEC;
	printf("Serial     :%f\n",duration);
	
//parallel 2
	s = clock();
	#pragma omp parallel for num_threads(2)
		for(n=0;n<1000000;n++)  
			comput(A,B,C);
	f = clock();
	duration = (double)(f - s)/CLOCKS_PER_SEC;
	printf("Parallel 2 :%f\n",duration);
	
	//parallel 4
	s = clock();
	#pragma omp parallel for num_threads(4)
		for(n=0;n<1000000;n++) 
			comput(A,B,C);
	f = clock();
	duration = (double)(f - s)/CLOCKS_PER_SEC;
	printf("Parallel 4 :%f\n",duration);

	s = clock();
	#pragma omp parallel for num_threads(8)
		for(n=0;n<1000000;n++)		
			comput(A,B,C);
    f = clock();
	duration = (double)(f - s)/CLOCKS_PER_SEC;
	printf("Parallel 8 :%f\n",duration);
	
	s = clock();
	#pragma omp parallel for num_threads(16)
		for(n=0;n<1000000;n++) 
			comput(A,B,C);
    f = clock();
	duration = (double)(f - s)/CLOCKS_PER_SEC;
	printf("Parallel 16 :%f\n",duration);
	 
	return 0;
}

这个程序其实非常的简单,它由一个compute函数作为核心运行代码组成,其用于实现两个矩阵的乘法。然后for循环循环执行 compute函数100w次,并统计执行这个循环的时间。

这个循环共有五份,分别使用单线程,2线程,4线程,8线程,16线程执行,并输出执行时间

二、问题分析

1.运行结果

运行结果如下

Serial      :0.051000
Parallel 2  :0.080000
Parallel 4  :0.082000
Parallel 8  :0.113000
Parallel 16 :0.164000

可以很明显的看到,随着线程数的增多,运行时间却也在增加,我也曾多次重新运行这个代码,最好的情况也不过是多线程的运行时间和单线程相同。并没有做到多线程的并行以减少时间的目标。

2.思考分析

其实明眼人一看代码就看得到代码本身是有漏洞的了,但是我当时并没有发现,还是后面做另一个实验发现的,这个代码其很大的线程安全问题。

它能够正常运行真的多亏了minGW的编译器能够自动优化上锁

说到上锁,这也是为什么我的代码在多线程的时候效率下降的原因:

在上述代码中,所有的线程都会对同一个内存数组 C 进行写操作,
学过操作系统的都知道,这个写操作是不能并行的,因为对同一个地址进行无序的写会导致结果的不可控,所以在这类操作时我们需要上锁,只有一个线程或者进程访问完之后,下一个进程或者线程才能够再访问,并且这个上锁的过程都是要花费时间和资源的。

我道这里才发现了,由于这个无法并行的写操作,导致众多的线程在这里都是“顺序执行”,而不是并行的,并且由于频繁的上锁操作,导致了更多的时间与资源的浪费,这才导致了一开始的错误


总结

今天的这个问题让我记住了,在编写并行程序时,一定要注意操作是否能够并行,尽量不要让多个线程同时访问相同的内存。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值