在OpenMP中使用共享数组时出现时间减慢问题

本文探讨了在OpenMP环境下进行并行计算时遇到的伪共享问题,详细解释了伪共享的原理及其对性能的影响。通过两个并行化实现的示例,展示了伪共享如何导致计算时间增加,并提出了减少伪共享的技术,包括使用专用数据和利用编译器优化。文章强调了在多处理器更新相邻共享数据时,如何通过调整数据分配和迭代映射来优化性能。
摘要由CSDN通过智能技术生成
#include "stdafx.h"
#include <time.h>
#include <windows.h>
#include "omp.h"

static long num_steps=100000000;//将区间[0,1]分成num_steps(N)份,即num_steps个小矩形
double step;
#define NUM_THREADS 2

int _tmain(int argc, _TCHAR* argv[])
{
	int i;
	clock_t t1,t2;
	double pi=0.0,sum[NUM_THREADS],sum2=0.0;

	step=1.0/(double) num_steps;//每个小矩形的宽是1/N,即step
	omp_set_num_threads(NUM_THREADS);

	t1=clock();
#pragma omp parallel 
	{
	   double x;
	   int id;
	   id=omp_get_thread_num();
	   double temp=0.0;

#pragma omp for
	   for(i=0;i<num_steps;i++){
	      x=(i+0.5)*step;
		  temp+= 4.0/(1.0+x*x);
	   }
	   sum[id]=temp;
	}
    
	for(i=0;i<NUM_THREADS;i++)
		pi+=sum[i]*step;
	t2=clock();
    printf("pi=%.15f\n",pi);
	printf("parallel time : %d\n",t2-t1);


	double x;
	t1=clock();
	for(int i=0;i<num_steps;i++){
	   x=(i+0.5)*step;
	   sum2= sum2+4.0/(1.0+x*x);
	}
	pi=sum2*step;
	t2=clock();

	printf("pi=%.15f\n",pi);
	printf("serial time : %d\n",t2-t1);


	system("pause");
	return 0;
}

在这里插入图片描述

#include "stdafx.h"
#include <time.h>
#include <windows.h>
#include "omp.h"

static long num_steps=100000000;//将区间[0,1]分成num_steps(N)份,即num_steps个小矩形
double step;
#define NUM_THREADS 2

int _tmain(int argc, _TCHAR* argv[])
{
	int i;
	clock_t t1,t2;
	double pi=0.0,sum[NUM_THREADS],sum2=0.0;

	step=1.0/(double) num_steps;//每个小矩形的宽是1/N,即step
	omp_set_num_threads(NUM_THREADS);

	t1=clock();
#pragma omp parallel 
	{
	   double x;
	   int id;
	   id=omp_get_thread_num();
	   sum[id]=0.0;

#pragma omp for
	   for(i=0;i<num_steps;i++){
	      x=(i+0.5)*step;
		  sum[id]+= 4.0/(1.0+x*x);
	   }
	}
    
	for(i=0;i<NUM_THREADS;i++)
		pi+=sum[i]*step;
	t2=clock();
    printf("pi=%.15f\n",pi);
	printf("parallel time : %d\n",t2-t1);


	double x;
	t1=clock();
	for(int i=0;i<num_steps;i++){
	   x=(i+0.5)*step;
	   sum2= sum2+4.0/(1.0+x*x);
	}
	pi=sum2*step;
	t2=clock();

	printf("pi=%.15f\n",pi);
	printf("serial time : %d\n",t2-t1);


	system("pause");
	return 0;
}

在这里插入图片描述

这两种方法并行化时间的差异主要是因为出现了伪共享问题。

伪共享及其避免方法

如果不慎将共享内存结构与 OpenMP 应用程序一起使用,可能导致性能下降且可伸缩性受限制。多个处理器更新内存中相邻共享数据将导致多处理器互连的通信过多,因而造成计算序列化。

1.什么是伪共享?

大多数高性能处理器(如 UltraSPARC 处理器)在 CPU 的低速内存和高速寄存器之间插入一个高速缓存缓冲区。访问内存位置时,会使包含所请求内存位置的一部分实际内存(缓存代码行)被复制到高速缓存中。随后可能在高速缓存外即可满足对同一内存位置或其周围位置的引用,直至系统决定有必要保持高速缓存和内存之间的一致性。

然而,同时更新来自不同处理器的相同缓存代码行中的单个元素会使整个缓存代码行无效,即使这些更新在逻辑上是彼此独立的。每次对缓存代码行的单个元素进行更新时,都会将此代码行标记为无效。其他访问同一代码行中不同元素的处理器将看到该代码行已标记为无效。即使所访问的元素未被修改,也会强制它们从内存或其他位置获取该代码行的较新副本。这是因为基于缓存代码行保持缓存一致性,而不是针对单个元素的。因此,互连通信和开销方面都将有所增长。并且,正在进行缓存代码行更新的时候,禁止访问该代码行中的元素。

这种情况称为伪共享。如果此情况频繁发生,OpenMP 应用程序的性能和可伸缩性就会显著下降。

在出现以下所有情况时,伪共享会使性能下降。

  • 由多个处理器修改共享数据。
  • 多个处理器更新同一缓存代码行中的数据。
  • 这种更新发生的频率非常高(例如,在紧凑循环中)。

请注意,在循环中只读状态的共享数据不会导致伪共享

2.减少伪共享

在执行应用程序时,对占据主导地位的并行循环进行仔细分析即可揭示伪共享造成的性能可伸缩性问题。通常可以通过以下方式减少伪共享

  • 尽可能使用专用数据;
  • 利用编译器的优化功能来消除内存负载和存储。

在特定情况下,如果处理较大的问题而共享较少,可能较难看到伪共享的影响。

处理伪共享的方法与特定应用程序紧密相关。在某些情况下,更改数据的分配方式可以减少伪共享。在其他情况下,通过更改迭代到线程的映射,为每个线程的每个块分配更多的工作(通过更改 chunksize 值),也可以减少伪共享。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值