简而言之,不,它不应该(并且不在我的机器上)。
首先,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,这意味着内部循环不会并行执行。
关于输出的注释,cout 和 cerr 都是线程安全的,但这仅适用于对运算符 << 的单个调用。因此,使用std::cout << i << " " << j << ... 之类的东西可以让您从不同的线程多次调用该运算符,并且可能会给您一些来自不同线程的交错部分(您可以自己尝试,它不会经常发生,但可以)。这就是为什么我们构造一个字符串,然后对<< 发出一次调用。
而且,在尝试并行运行某些东西时,您希望在每个级别中使用多少线程将取决于它们将执行的操作类型以及您正在运行它的机器。如果您想获得最佳性能,最好尝试一下并玩一下。只需确保线程总数(嵌套循环中线程的乘积)不超过系统上可用的线程数,这通常会减慢速度。