C++ 如何解决多线程代码中for循环中的依赖关系?
我无法使用OpenMP解决for循环中的依赖项,因此程序将执行得更快。这就是我如何做到的,它的工作原理,但我需要一个更快的解决方案。有人知道怎么做才能更快地工作吗C++ 如何解决多线程代码中for循环中的依赖关系?,c++,multithreading,parallel-processing,openmp,C++,Multithreading,Parallel Processing,Openmp,我无法使用OpenMP解决for循环中的依赖项,因此程序将执行得更快。这就是我如何做到的,它的工作原理,但我需要一个更快的解决方案。有人知道怎么做才能更快地工作吗 #pragma omp parallel for num_threads(tc) ordered schedule(dynamic, 1) private(i) shared(openSet, maxVal, current, fScores) for(i = 0;i < openSet.size
#pragma omp parallel for num_threads(tc) ordered schedule(dynamic, 1) private(i) shared(openSet, maxVal, current, fScores)
for(i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] < maxVal){
#pragma omp ordered
maxVal = fScores[openSet[i].x * dim + openSet[i].y];
current = openSet[i];
}
}
首先,你需要确定,这是你的热点。然后给我们一个合适的测试套件,以确保您实际获得性能。使用“google_benchmark”等工具。确保您是在发布模式下编译的,否则您的度量将被完全破坏 这就是说,我认为你正在寻找最大的减少
#pragma omp parallel for reduction(max : maxVal )
for(i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] > maxVal){
maxVal = fScores[openSet[i].x * dim + openSet[i].y];
}
}
#pragma omp并行减少(max:maxVal)
对于(i=0;imaxVal){
maxVal=fScores[openSet[i].x*dim+openSet[i].y];
}
}
“当前”接缝是多余的。我认为这种比较混淆了
您能否以线性方式访问“fScores”中的数据。使用“openSet”上的间接寻址将有大量缓存未命中。如果您能够以某种方式摆脱这种间接方式,那么在单线程和多线程的情况下,您将获得很高的加速比
在第二个循环中,“推回”会破坏你的表现。我也有类似的问题。对我来说,这是非常有益的
- 创建具有最大可能长度的向量
- 用空值初始化它
- 使用符合标准的openmp正确设置
- 使用向量时,检查空值
#pragma omp parallel for reduction(max : maxVal )
for(i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] > maxVal){
maxVal = fScores[openSet[i].x * dim + openSet[i].y];
}
}
#pragma omp并行减少(max:maxVal)
对于(i=0;imaxVal){
maxVal=fScores[openSet[i].x*dim+openSet[i].y];
}
}
“当前”接缝是多余的。我认为这种比较混淆了
您能否以线性方式访问“fScores”中的数据。使用“openSet”上的间接寻址将有大量缓存未命中。如果您能够以某种方式摆脱这种间接方式,那么在单线程和多线程的情况下,您将获得很高的加速比
在第二个循环中,“推回”会破坏你的表现。我也有类似的问题。对我来说,这是非常有益的
- 创建具有最大可能长度的向量
- 用空值初始化它
- 使用符合标准的openmp正确设置
- 使用向量时,检查空值
- 在我看来,您似乎误解了OpenMP ordered子句的实际作用。从中可以看出:
有序构造可以指定
工作共享循环、simd或工作共享循环simd区域将
按照循环迭代的顺序执行,或者它是独立的
指令,指定doacross中的交叉迭代依赖项
环巢。有序构造对
执行有序区域,同时允许区域外的代码
并行运行
:
ordered子句的工作原理如下:执行不同的线程
同时,直到它们遇到有序区域,然后
按顺序执行,其顺序与在
串行循环
根据您使用它的方式,似乎您将ORDER子句误认为是OpenMP子句:
关键构造限制了关联操作的执行
一次将结构化块转换为单个线程
所以,使用ordered子句,您的代码基本上是按顺序运行的,并带有额外的并行开销。然而,即使您使用了关键构造函数,开销也会太高,因为线程会在每次循环迭代中锁定
对于第一个循环,乍一看,您可以使用OpenMP
reduce
子句(即reduce(max:maxVal)
),它可以从标准中读取:
reduce子句可用于执行某些形式的重复
并行计算(…)。用于并行计算和工作共享
构造时,将创建每个列表项的私有副本,每个列表项对应一个副本
隐式任务,就好像使用了private子句一样。(……)该
然后按照上面的指定初始化私有副本。最后
为其指定缩减子句的区域,原始列表
项目通过将其原始值与最终值相结合进行更新
使用指定的
还原标识符
关于减损条款如何工作的更详细的解释,请看下面的例子
尽管如此,您仍在更新两个变量,即maxVal
和current
。因此,仅使用reduce子句就很难解决这些依赖关系。尽管如此,一种方法是在线程之间创建共享数据结构,其中每个线程更新该共享结构的给定位置。在并行区域的末尾,主线程相应地更新maxVal
和current
的原始值
因此,不是:
#pragma omp parallel for num_threads(tc) ordered schedule(dynamic, 1) private(i) shared(openSet, maxVal, current, fScores)
for(i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] < maxVal){ // <-- you meant '>' not '<'
#pragma omp ordered
maxVal = fScores[openSet[i].x * dim + openSet[i].y];
current = openSet[i];
}
}
在我看来,您误解了OpenMP ORDER子句的实际功能。从中可以看出: 有序构造可以指定 工作共享循环、simd或工作共享循环simd区域将 按照循环迭代的顺序执行,或者它是独立的 指定交叉迭代de的指令
#pragma omp parallel for num_threads(tc) ordered schedule(dynamic, 1) private(i) shared(openSet, maxVal, current, fScores)
for(i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] < maxVal){ // <-- you meant '>' not '<'
#pragma omp ordered
maxVal = fScores[openSet[i].x * dim + openSet[i].y];
current = openSet[i];
}
}
int shared_maxVal[tc] = {INT32_MAX};
int shared_current[tc] = {0};
#pragma omp parallel num_threads(tc)
{
int threadID = omp_get_thread_num();
#pragma omp for shared(openSet, fScores)
for(int i = 0;i < openSet.size();i++){
if(fScores[openSet[i].x * dim + openSet[i].y] > shared_maxVal[threadID]){
shared_maxVal[threadID] = fScores[openSet[i].x * dim + openSet[i].y];
shared_current[threadID] = openSet[i];
}
}
}
for(int i = 0; i < tc; i++){
if(maxVal < shared_maxVal[i]){
maxVal = shared_maxVal[i];
current = shared_current[i];
}
}
#pragma omp parallel for num_threads(tc) ordered schedule(dynamic, 1) private(i) shared(neighbours, openSet, gScores, fScores, tentative_gScore)
for(i = 0;i < neighbours.size();i++){
#pragma omp ordered
tentative_gScore = gScores[current.x * dim + current.y] + 1;
if(tentative_gScore < gScores[neighbours[i].x * dim + neighbours[i].y]){
cameFrom[neighbours[i].x * dim + neighbours[i].y] = current;
gScores[neighbours[i].x * dim + neighbours[i].y] = tentative_gScore;
fScores[neighbours[i].x * dim + neighbours[i].y] = tentative_gScore + hScore(); //(p.x, p.y, xEnd, yEnd)
if(contains(openSet, neighbours[i]) == false){
openSet.push_back(neighbours[i]);
}
}
}
// Create an array of "openSets" let us named "shared_openSet"
#pragma omp parallel num_threads(tc)
{
int threadID = omp_get_thread_num();
#pragma omp for shared(neighbours, gScores, fScores)
for(int i = 0;i < neighbours.size();i++){
// I just assume the type in but you can change if for the real type
int tentative_gScore = gScores[current.x * dim + current.y] + 1;
if(tentative_gScore < gScores[neighbours[i].x * dim + neighbours[i].y]){
cameFrom[neighbours[i].x * dim + neighbours[i].y] = current;
gScores[neighbours[i].x * dim + neighbours[i].y] = tentative_gScore;
fScores[neighbours[i].x * dim + neighbours[i].y] = tentative_gScore + hScore();
if(contains(openSet, neighbours[i]) == false){
shared_openSet[threadID].push_back(neighbours[i]);
}
}
}
}
// merge all the elements from shared_openSet into openSet.