您可以将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的编译器。这就排除了MS VC++的所有版本(甚至是VS2012中的版本),如果你碰巧在Windows上开发的话。
如果您希望每个线程只有一个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
指令指示编译器将其放入线程本地存储器,例如,使它成为每个线程的全局变量。这些持续存在于不同的平行区域之间。您也可以在static
局部变量上使用omp threadprivate
。 (但仅在相同的功能)不同的并行区域之间的这些太坚持:
#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
环境变量指定的每个并行区域将总是与尽可能多的线程中执行。
非常感谢您的回答。但问题比这更复杂一点。我有一个具有成员函数来完成拟合的类。如果同时使用多个线程,则类实例不是线程安全的。我能想到的解决方案是每个线程都有一个适配类的实例;但我不知道如何做到这一点。你能否告诉我是否有可能告诉程序每个线程必须接受一个类的prive实例,并用一些函数init(...)初始化它,并使用正确的参数,我知道每个数据集都适合我? –
还有一件事。为了提高效率,我希望在程序开始时只运行init(...)一次。它包含一些非常昂贵的矩阵分配。谢谢! –
所有声明_inside_并行区域的变量都是线程本地的,即每个线程都获得这些变量的私有副本。声明_outside_(和之前)并行区域的变量默认共享,除非'omp parallel'构造中指定了'private(varname)'子句。我会修改我的答案以反映这一点。 –