Fra*_*cue 5 multithreading for-loop openmp while-loop c++11
我有一个运行多次迭代的代码,只有满足条件时,才会保存迭代的结果。这自然地表示为 while 循环。我试图使代码并行运行,因为每个实现都是独立的。所以我有这个:
while(nit<avit){
#pragma omp parallel shared(nit,avit)
{
//do some stuff
if(condition){
#pragma omp critical
{
nit++;
\\save results
}
}
}//implicit barrier here
}
Run Code Online (Sandbox Code Playgroud)
这工作正常...但是每次实现之后都会存在一个障碍,这意味着如果我在并行块内所做的事情在一次迭代中比其他迭代花费的时间更长,那么我的所有线程都在等待它完成,而不是继续下一次迭代。
有没有办法避免这种障碍,使线程继续工作?我平均进行了数千次迭代,所以再进行几次迭代也不会造成什么影响(如果变量nit在已经运行的线程中没有增加)...
我试图将其变成并行的 for,但是 for 循环中的自动增量使变量nit变得疯狂。这是我的尝试:
#pragma omp parallel shared(nit,avit)
{
#pragma omp for
for(nit=0;nit<avit;nit++){
//do some stuff
if(condition){
\\save results
} else {
#pragma omp critical
{
nit--;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,它继续工作并围绕 for 循环进行,但我的nit变量采用不可预测的值...正如人们可以从不同线程在不同时间增加和减少它所期望的那样。
我还尝试将 for 循环中的增量留空,但它无法编译,或者试图欺骗我的代码,使其在 for 循环中没有增量,例如
...
incr=0;
for(nit=0;nit<avit;nit+=incr)
...
Run Code Online (Sandbox Code Playgroud)
但后来我的代码崩溃了......
有任何想法吗?
谢谢
编辑:这是 while 循环上代码的最小工作示例:
#include <random>
#include <vector>
#include <iostream>
#include <time.h>
#include <omp.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main(){
int nit,dit,avit=100,t,j,tmax=100,jmax=10;
vector<double> Res(10),avRes(10);
nit=0; dit=0;
while(nit<avit){
#pragma omp parallel shared(tmax,nit,jmax,avRes,avit,dit) private(t,j) firstprivate(Res)
{
srand(int(time(NULL)) ^ omp_get_thread_num());
t=0; j=0;
while(t<tmax&&j<jmax){
Res[j]=rand() % 10;
t+=Res[j];
if(omp_get_thread_num()==5){
usleep(100000);
}
j++;
}
if(t<tmax){
#pragma omp critical
{
nit++;
for(j=0;j<jmax;j++){
avRes[j]+=Res[j];
}
for(j=0;j<jmax;j++){
cout<<avRes[j]/nit<<"\t";
}
cout<<" \t nit="<<nit<<"\t thread: "<<omp_get_thread_num();
cout<<endl;
}
} else{
#pragma omp critical
{
dit++;
cout<<"Discarded: "<<dit<<"\r"<<flush;
}
}
}
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我添加了该usleep部分来模拟一个线程比其他线程花费更长的时间。如果运行该程序,所有线程都必须等待线程 5 完成,然后才能开始下一次运行。我想要做的正是避免这种等待,即我希望其他线程选择下一个迭代而不等待 5 完成。
您基本上可以遵循与此问题相同的概念,但略有不同,以确保avRes不会并行写入:
int nit = 0;
#pragma omp parallel
while(1) {
int local_nit;
#pragma omp atomic read
local_nit = nit;
if (local_nit >= avit) {
break;
}
[...]
if (...) {
#pragma omp critical
{
#pragma omp atomic capture
local_nit = ++nit;
for(j=0;j<jmax;j++){
avRes[j] += Res[j];
}
for(j=0;j<jmax;j++){
// technically you could also use `nit` directly since
// now `nit` is only modified within this critical section
cout<<avRes[j]/local_nit<<"\t";
}
}
} else {
#pragma omp atomic update
dit++;
}
}
Run Code Online (Sandbox Code Playgroud)
它也适用于关键区域,但原子效率更高。
您还需要考虑另一件事,rand()不应该在并行上下文中使用。看到这个问题。对于 C++,使用来自 的私有(即在并行区域内定义)随机数生成器<random>。