循环的OpenMP和C++并行:为什么我的代码在使用OpenMP时会变慢
OpenMP and C++ parallel for loop: why does my code slow down when using OpenMP?
我有一个关于使用OpenMP(与C++一起使用)的简单问题,我希望有人能帮助我。下面我举了一个小例子来说明我的问题。
#include<iostream>
#include<vector>
#include<ctime>
#include<omp.h>
using namespace std;
int main(){
srand(time(NULL));//Seed random number generator
vector<int>v;//Create vector to hold random numbers in interval [0,9]
vector<int>d(10,0);//Vector to hold counts of each integer initialized to 0
for(int i=0;i<1e9;++i)
v.push_back(rand()%10);//Push back random numbers [0,9]
clock_t c=clock();
#pragma omp parallel for
for(int i=0;i<v.size();++i)
d[v[i]]+=1;//Count number stored at v[i]
cout<<"Seconds: "<<(clock()-c)/CLOCKS_PER_SEC<<endl;
for(vector<int>::iterator i=d.begin();i!=d.end();++i)
cout<<*i<<endl;
return 0;
}
上面的代码创建了一个向量v
,该向量包含[0,9]
范围内的10亿个随机整数。然后,代码循环通过v
,计算每个不同整数有多少个实例(即,在v中找到了多少个1,有多少个2,等等)
每次遇到特定整数时,都通过递增向量d
的适当元素来对其进行计数。那么,d[0]
计算多少个零,d[6]
计算多少个六,等等。到目前为止有意义吗?
我的问题是当我试图使计数循环平行时。如果没有#pragma OpenMP
语句,我的代码需要20秒,而使用pragma
,它需要60秒钟。
很明显,我误解了一些与OpenMP相关的概念(也许是如何共享/访问数据?)。有人能解释一下我的错误吗?或者用合适的关键词为我指明一些有见地的文献的方向,以帮助我搜索?
您的代码exibits:
- 由于对共享变量的未加密访问而导致的竞争条件
- 虚假和真实共享缓存问题
- 运行时间测量错误
由于在多个线程中同时更新向量d
的相同元素,因此会出现争用情况。注释掉srand()
行,并使用相同数量的线程(但使用多个线程)多次运行代码。比较不同运行的输出。
当两个线程向彼此接近的内存位置写入时,会出现错误共享,从而导致在同一缓存线上。这导致在多套接字系统中,缓存线不断地从一个内核跳到另一个内核或从CPU跳到CPU,并导致缓存一致性消息过多。在每个高速缓存行32个字节的情况下,矢量的8个元素可以放在一个高速缓存行中。在每个高速缓存行64个字节的情况下,整个向量d
适合于一个高速缓存行。这使得代码在Core 2处理器上速度较慢,在Nehalem和后Nehalem(例如Sandy Bridge)处理器上速度略慢(但不如Core 2慢)。真正的共享发生在两个或多个线程同时访问的那些元素上。您应该将增量放在OpenMP atomic
构造中(较慢),使用OpenMP锁数组来保护对d
元素的访问(较快或较慢,取决于您的OpenMP运行时),或者累积本地值,然后进行最终的同步缩减(最快)。第一个是这样实现的:
#pragma omp parallel for
for(int i=0;i<v.size();++i)
#pragma omp atomic
d[v[i]]+=1;//Count number stored at v[i]
第二个是这样实现的:
omp_lock_t locks[10];
for (int i = 0; i < 10; i++)
omp_init_lock(&locks[i]);
#pragma omp parallel for
for(int i=0;i<v.size();++i)
{
int vv = v[i];
omp_set_lock(&locks[vv]);
d[vv]+=1;//Count number stored at v[i]
omp_unset_lock(&locks[vv]);
}
for (int i = 0; i < 10; i++)
omp_destroy_lock(&locks[i]);
(包括访问omp_*
功能的omp.h
)
我把第三种选择的实施留给你。
您使用clock()
测量运行时间,但它测量的是CPU时间,而不是运行时如果有一个线程以100%的CPU使用率运行1秒,那么clock()
将表示CPU时间增加1秒。如果有8个线程以100%的CPU使用率运行1秒,clock()
将表示CPU时间增加8秒(即8个线程乘以每个线程1 CPU秒)。请改用omp_get_wtime()
或gettimeofday()
(或某些其他高分辨率计时器API)。
编辑一旦您的比赛条件通过正确的同步得到解决,那么以下段落适用,在此之前,您的数据比赛条件不幸地使速度比较静音:
您的程序正在放慢速度,因为在pragma部分有10个可能的输出,这些输出是随机访问的。因此,如果没有锁(您需要通过同步提供锁),OpenMP就无法访问这些元素中的任何一个,并且锁将导致线程的开销高于并行计数的开销。
加快速度的一个解决方案是为每个OpenMP线程创建一个局部变量,该变量统计特定线程看到的所有0-10值。然后在主计数向量中求和。这将很容易并行化,而且速度更快,因为线程不需要锁定共享写向量。我希望速度接近Nx,其中N是来自OpenMP的线程数,因为所需的锁定应该非常有限。这个解决方案还避免了当前代码中的许多竞争条件。
请参阅http://software.intel.com/en-us/articles/use-thread-local-storage-to-reduce-synchronization/有关线程本地OpenMP 的更多详细信息
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 对于openMP来说,有什么建议可以将以下代码与openMP并行
- 共享或私有 openmp 代码中的结构化类型变量
- 为什么这段代码(在 Matlab 的 MEX 文件中使用 OpenMP)给出不同的结果?
- OpenMP 比串行代码慢 100 倍
- 为什么每次在 openmp 代码中获得不同的输出?
- OpenMP 代码已中止
- 退出 OpenMP 代码时出现分段错误
- 为什么要在我的OpenMP代码中增加执行时间
- C++OpenMP代码内存泄漏
- OpenMP 代码C++比 c++ 慢
- 在 OpenMP 代码C++测量执行时间
- OpenMP代码帮助
- OpenMP代码大部分时间都在Join Barrier上等待
- 如何提高OpenMP代码的性能?
- 我的openmp代码不能在服务器上工作
- OpenMP代码远慢于串行内存或线程开销瓶颈
- 为什么我的OpenMP c++代码比串行代码慢
- 从 pthread调用的 OpenMP 代码的性能问题
- 粒子模拟的并行化OpenMP代码性能差