【问题标题】:OpenMP nested for loopOpenMP 嵌套 for 循环
【发布时间】:2018-08-11 14:32:33
【问题描述】:

您好,我有以下代码。内部循环(函数 Foo 中的 for 循环)是否提供并行性能?或者我是否必须在外循环中添加诸如“折叠”之类的内容才能获得内循环的并行性能?

void X(int i)
{
}
void Foo()
{
   ... Do something
   #pragma omp parallel for
   for( int i = 0; i < 10000; ++i )
   {
       X(i);
   }
}
void main()
{
   #pragma omp parallel for
   for( int k = 0; k < 1000; ++k )
   {
     Foo();
   }
}

【问题讨论】:

    标签: for-loop nested openmp


    【解决方案1】:

    简而言之,不,它不应该(并且不在我的机器上)。

    首先,OpenMP 有一个值来设置它将使用的线程数,默认情况下它设置为系统上可用的线程数。因此,即使这会起作用,第一次调用也将使用所有可用线程,因此第二次调用将按顺序执行。这个值可以通过调用omp_set_num_threads(...)来修改,但它是全局的,所以上面的行为会保持不变,即第一次调用会使用它认为可用的所有线程。解决此问题的最简单方法是将num_threads(...) 添加到 omp pragma 并强制它忽略该值。当然你也可以在其中使用变量,所以num_threads(x) 是完全合法的,其中x 是一些在编译时甚至不需要知道的整数。

    #pragma omp parallel for num_threads(2) //Let's say we want 2 threads in this for loop
    

    但是,仅此一项是不够的,因为默认情况下嵌套是禁用的。要启用 OMP 并行区域的嵌套,我们只需调用 omp_set_nested(true);。添加omp_set_max_active_levels(2); 也可能是明智的,但这不是必需的,默认值似乎更大(我实际上不知道它是什么,可能只是无限)。

    所以,一个简单的测试程序可能是这样的:

    #include <omp.h>
    #include <iostream>
    #include <string>
    
    int main(void) {
        omp_set_nested(true); //Enables nesting. 
    #pragma omp parallel for num_threads(2) //We will be using 2 threads for the first for loop.
        for (int i = 0; i < 10; i++) {
            int tid1 = omp_get_thread_num();
    #pragma omp parallel for num_threads(2) //And 2 threads for each of the threads in the second loop.
            //This will require a total of 4 threads. 
            for (int j = 0; j < 10; j++) {
                int tid2 = omp_get_thread_num();
                std::string msg =  std::to_string(i) + " " + std::to_string(j) + " " + std::to_string(tid1) + " " + std::to_string(tid2) + "\n";
                std::cerr << msg;
            }
        }
        return 0;
    }
    

    这将告诉使用哪个线程组合(tid1, tid2) 运行(i, j) 的每个组合,输出格式为i j tid1 tid2。如果我们注释掉omp_set_nested(true);,我们会注意到tid2 总是0,这意味着内部循环不会并行执行。

    关于输出的注释,coutcerr 都是线程安全的,但这仅适用于对运算符 &lt;&lt; 的单个调用。因此,使用std::cout &lt;&lt; i &lt;&lt; " " &lt;&lt; j &lt;&lt; ... 之类的东西可以让您从不同的线程多次调用该运算符,并且可能会给您一些来自不同线程的交错部分(您可以自己尝试,它不会经常发生,但可以)。这就是为什么我们构造一个字符串,然后对&lt;&lt; 发出一次调用。

    而且,在尝试并行运行某些东西时,您希望在每个级别中使用多少线程将取决于它们将执行的操作类型以及您正在运行它的机器。如果您想获得最佳性能,最好尝试一下并玩一下。只需确保线程总数(嵌套循环中线程的乘积)不超过系统上可用的线程数,这通常会减慢速度。

    【讨论】:

    • 如果我在函数内部设置omp_set_max_active_levels(2),它是否会在整个代码中生效,即它是否也会在单独的函数中生效?
    • 首先是免责声明,我实际上并不知道 OpenMP 是如何实现的,但我们假设它会影响您使用的任何 OMP 库定义的一些全局变量,这听起来很合理。在这种情况下,它应该可以正常工作。也就是说,我会避免在任何并行区域内调用它,而不查看一些文档以确保没有发生任何奇怪的事情。
    • 据:openmp.org/spec-html/5.0/openmpsu125.html我之前说的可能是真的。
    • 我在顺序区域中调用它,并且我希望在每个相关函数中都发生嵌套并行,而不是每次都调用它。 max_active_levels 的默认值是 1 还是无限制
    • 这可能是实现定义,但检查系统的一种简单方法是在设置任何内容之前调用omp_get_max_active_levels()
    猜你喜欢
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2017-04-20
    相关资源
    最近更新 更多