嗨,我有以下代码。内部循环(函数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();
}
}
答案 0 :(得分:0)
简而言之,不,它不应该(并且不在我的机器上)。
首先,OpenMP具有一个值,该值设置它将使用的线程数,默认情况下,它设置为系统上可用的线程数。因此,即使这行之有效,第一个调用将已经使用了所有可用线程,因此第二个调用将被顺序执行。可以通过调用omp_set_num_threads(...)
来修改此值,但是它是全局的,因此上述行为将保留,即,第一次调用将使用它认为可用的所有线程。解决此问题的最简单方法是在omp编译指示中添加num_threads(...)
并强制其忽略该值。当然,您也可以在其中使用变量,因此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
,这意味着内部循环不会并行执行。
关于输出的说明cout
和cerr
都是线程安全的,但是仅适用于对运算符<<
的单次调用。因此,使用类似std::cout << i << " " << j << ...
之类的方法,可以使您从不同的线程对该操作符进行大量调用,并且可以从不同的线程中获得一些交错的部分(您可以尝试一下,它不会经常发生,但是可以)。这就是为什么我们构造一个字符串,然后发出对<<
的单个调用的原因。
并且,与往常一样,当尝试并行运行某项内容时,您希望在每个级别中使用多少个线程将取决于它们将要执行的操作类型以及所运行的计算机。如果您想获得最佳性能,最好尝试一下并尝试一下。只要确保线程总数(嵌套循环中线程的乘积)不超过系统上可用的线程数,通常会降低速度。