您可以将 OpenMP 并行用于动态计划或 OpenMP 任务。两者都可以用于并行化每次迭代需要不同时间来完成的情况。动态安排:
#pragma omp parallel
{
Fitter fitter;
fitter.init();
#pragma omp for schedule(dynamic,1)
for (int i = 0; i < numFits; i++)
fitter.fit(..., &results[i]);
}
schedule(dynamic,1)
使每个线程一次执行一次迭代,并且线程永远不会闲置,除非没有更多的迭代要处理。
有任务:
#pragma omp parallel
{
Fitter fitter;
fitter.init();
#pragma omp single
for (int i = 0; i < numFits; i++)
{
#pragma omp task
fitter.fit(..., &results[i]);
}
#pragma omp taskwait
// ^^^ only necessary if more code before the end of the parallel region
}
这里有一个线程运行一个 for 循环,产生 1000 个 OpenMP 任务。OMP 任务保存在队列中并由空闲线程处理。它的工作方式有点类似于动态 for 循环,但在代码结构中允许更大的自由度(例如,对于可以并行递归算法的任务)。该taskwait
构造等待所有待处理的任务完成。它隐含在并行区域的末尾,因此只有在并行区域结束之前有更多代码时才真正有必要。
在这两种情况下,每次调用都fit()
将在不同的线程中完成。您必须确保拟合一组参数不会影响拟合其他组,例如,这fit()
是一种线程安全的方法/函数。这两种情况还要求执行时间fit()
远高于 OpenMP 构造的开销。
OpenMP 任务需要符合 OpenMP 3.0 的编译器。如果您碰巧在 Windows 上开发,这排除了所有版本的 MS VC++(甚至是 VS2012 中的版本)。
如果您希望每个线程只初始化一个 fitter 实例,那么您应该采取一些不同的方法,例如使 fitter 对象成为全局对象并且threadprivate
:
#include <omp.h>
Fitter fitter;
#pragma omp threadprivate(fitter)
...
int main()
{
// Disable dynamic teams
omp_set_dynamic(0);
// Initialise all fitters once per thread
#pragma omp parallel
{
fitter.init();
}
...
#pragma omp parallel
{
#pragma omp for schedule(dynamic,1)
for (int i = 0; i < numFits; i++)
fitter.fit(..., &results[i]);
}
...
return 0;
}
这fitter
是Fitter
该类的全局实例。该omp threadprivate
指令指示编译器将其放入线程本地存储中,例如使其成为每个线程的全局变量。这些在不同的平行区域之间持续存在。您也可以omp threadprivate
在static
局部变量上使用。这些也在不同的并行区域之间持续存在(但仅在相同的函数中):
#include <omp.h>
int main()
{
// Disable dynamic teams
omp_set_dynamic(0);
static Fitter fitter; // must be static
#pragma omp threadprivate(fitter)
// Initialise all fitters once per thread
#pragma omp parallel
{
fitter.init();
}
...
#pragma omp parallel
{
#pragma omp for schedule(dynamic,1)
for (int i = 0; i < numFits; i++)
fitter.fit(..., &results[i]);
}
...
return 0;
}
该调用禁用动态团队,即每个并行区域将始终使用环境变量omp_set_dynamic(0)
指定的线程数执行。OMP_NUM_THREADS