OpenMP -简单循环,但仍然是无限的

OpenMP - Easy Loop, but still infinite?

本文关键字:仍然是 无限 简单 单循环 OpenMP      更新时间:2023-10-16

我在c++代码中使用OpenMP有一个非常奇怪的问题:

void update(double *source, double *target, int n)
{
    target[0] = source[0];
    target[n-1] = source[n-1];
    #pragma omp parallel for 
    for(int i = 1; i < n-1; ++i) 
        target[i] = (1.0/3.0) * (source[i-1] + source[i] + source[i+1]);
}

源和目标都是有n个元素的双精度数组。在没有OpenMP的情况下,代码可以正常工作。但是只要我使用了编译器,代码就会卡在这个循环中。问题是:我完全不知道为什么。希望有人能帮助我

n有多大?

OpenMP parallel for指令的默认调度是特定于实现的。看起来在GOMP (gcc使用的OpenMP实现)中,根据这里的文档,默认值是(dynamic,1)。这意味着每个线程都在访问(在i-1i+1)由相邻线程加载的内存位置,这可能导致缓存利用率低下。在现代CPU体系结构上,这样的模板操作通常是内存受限的,并且对缓存很敏感。您可以尝试使用更大的块指定调度,例如:

#pragma omp parallel for schedule(dynamic,1024)

我只是用1024在这里作为一个例子。在实践中,您应该通过实验来找到最优的分块因子(或者使用参数扫描进行系统搜索,这个过程通常称为"自动调优")。或者你可以选择一个更理论化的值,例如,通过从CPU的L1或L2缓存大小中得出它。

或者您可以尝试静态调度,因为for循环内的计算量在线程之间是一致的,并且动态调度程序的开销可能会导致瓶颈。如果指定

#pragma omp parallel for schedule(static)

没有块大小,那么每个线程将被分配一个大小大致相同的块。

最后,您可能还希望将OpenMP线程固定在它们自己的CPU内核上。您可以使用GOMP_CPU_AFFINITY环境变量来完成此操作。

编辑:

我只是在玩下面用gcc 4.2.1编译的测试程序,我认为我上面链接到的文档是不正确的。看起来GOMP默认设置为schedule(static)

#include <stdio.h>
#include <omp.h>
int main(int argc, char** argv)
{
    int i;
    #pragma omp parallel for
    for (i=0; i<15; i++) {
        int id = omp_get_thread_num();
        printf("%d assigned to thread %dn", i, id);
    }
}

两个线程的输出是:

$ ./test_sched | sort -n
0 assigned to thread 0
1 assigned to thread 0
2 assigned to thread 0
3 assigned to thread 0
4 assigned to thread 0
5 assigned to thread 0
6 assigned to thread 0
7 assigned to thread 0
8 assigned to thread 1
9 assigned to thread 1
10 assigned to thread 1
11 assigned to thread 1
12 assigned to thread 1
13 assigned to thread 1
14 assigned to thread 1