【发布时间】:2021-10-18 18:10:54
【问题描述】:
情况:
我正在尝试编写偏微分方程的求解器。特别是它的并行实现(通过域分解)。该代码产生正确的结果。但是,我在使用 OpenMp 时遇到了一些问题,因为我必须在另一个 while 循环中执行多个 for 循环。在此之前,我没有任何使用 OpenMP 的经验,所以我有点不确定。我正在一个示例中测试代码,该示例应尽可能减少负载不平衡。在使用 4 个线程进行测试期间,我观察到所有 CPU 内核的使用几乎相同。
我有一个 intel i5-6500 4x3.2 CPU,我在 Windows 上使用 Clion 和 cygwin 2.11.1 (gcc)、cmake 3.19 和 C++20 以及编译器标志 -fopenmp 进行编译
程序的主要部分如下所示:
FunctionData data_array[N];
bool flag=true;
for (int i = 0; i < N; ++i){
function1(data_array[i]);
}
while(flag){
if(cancellation_criterium(data_array)){
flag= false;
}
for (int i = 0; i < N; ++i){
function2(data_array[i]);
}
for (int i = 0; i < N; ++i){
function3(data_array[i]);
}
}
每个子域的数据都在data_array 的相应条目中。应该没有数据竞争,每个函数都是一个void-函数,它修改了data_array[i],它是通过引用传递的。我检查了所有的结果,它们都是正确的。
在cancellation_criterium 中,我想找到 FunctionData 的一些成员变量的最小值,因此我需要访问所有数组。
while循环执行了很多,我正在测试一个负载平衡问题应该很少的示例。
每个函数都可以并行执行,但它们必须一个接一个地执行(首先是函数 2 的所有迭代,然后是函数 3)。我确保没有数据竞争,每个功能只需要
由于我是 OpenMP 的新手,我的第一个想法是在每个 for 循环之前添加 #pragma omp parallel for。然而,这极大地(2-3 倍)增加了程序的运行时间。在与一些朋友交谈并在网上做了一些研究后,我得出结论,在 while 循环中不断创建和加入线程可能是造成这种情况的原因。
我的第二种方法是创建一个环绕的#omp parallel 区域。结果代码是:
FunctionData data_array[N];
bool flag=true;
#pragma omp parallel shared(data_array, flag)
{
#pragma omp for
for (int i = 0; i < N; ++i){
function1(data_array[i]);
}
#pragma omp barrier
while(flag){
#pragma omp master
{
if(cancellation_criterium(data_array)){
flag= false;
}
}
#pragma omp barrier
#pragma omp for
for (int i = 0; i < N; ++i){
function2(data_array[i]);
}
#pragma omp barrier
#pragma omp for
for (int i = 0; i < N; ++i){
function3(data_array[i]);
}
#pragma omp barrier
}
}
这也遭受了与第一次尝试类似的性能损失。我还尝试使用函数 2 和 3 在 for 循环周围设置平行区域,但无济于事。
在我最后一次尝试中,我使用了并行区域,但在其中使用了#pragma omp for-loops。尽管我认为这不应该工作,因为我认为我创建了嵌套的并行区域,我一直观察到性能略有提高。
代码如下:
FunctionData data_array[N];
bool flag=true;
#pragma omp parallel shared(data_array, flag)
{
#pragma omp master
#pragma omp parallel for
for (int i = 0; i < N; ++i){
function1(data_array[i]);
}
#pragma omp master
while(flag){
if(cancellation_criterium(data_array)){
flag= false;
}
#pragma omp parallel for
for (int i = 0; i < N; ++i){
function2(data_array[i]);
}
#pragma omp parallel for
for (int i = 0; i < N; ++i){
function3(data_array[i]);
}
}
}
问题
1. 尝试 3 的表现比尝试 1 好得多,或者你有什么想法为什么应该这样?我无法理解最后一次尝试如何创建和加入比第一个版本更少的线程,即使它可以更快。
2. 在第二次尝试中,当代码到达while(flag) 时,并行区域中的每个线程是否执行为并行区域中的每个线程创建的这个while 循环?我怀疑这可能会发生,但程序的结果是正确的。
3. 有没有更聪明的方法可以用 OpenMP 解决这个问题?
原始代码的长度相当长,但如果你想看看,我很乐意给你github页面的链接。
---edit--在第一次尝试时添加了#pragma omp parallel for,N是2或4。
【问题讨论】:
-
N有多大,function1..3需要多少时间?您在每个 for 循环之前都写了#pragma omp parallel,这是缺少for的错字吗?如果没有,首先尝试在每个 for 循环之前使用#pragma omp parallel for。 -
你说你认为负载应该是平衡的,但测试不同的调度永远不会有坏处。用
schedule(dynamic)和/或schedule(guided)试试看是否有任何改进。 -
@Laci 是的,这是一个错字,谢谢。 N 是 2 或 4,非常小
-
好吧,那就是你的问题。如果您只有 2-4 次循环迭代,您希望如何在线程之间分配迭代?
-
我做了一些测量,发现使用#pragma omp parallel 时,function2 的循环时间要长 3-4 倍,而其他循环的运行时间会减少。这也占用了总运行时间的 95% 以上。
标签: c++ multithreading openmp