我有以下代码来迭代std::tuple
。代码来自here。
#include <tuple>
#include <utility>
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT) // Unused arguments are given no names.
{ }
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT& f)
{
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
现在,我想用openmp执行这个for_each
循环,就像我在for
上使用openmp一样。是否有可能使这成为可能?
注意:您可以修改上述代码或使用您自己for_each
的任何其他版本。
答案 0 :(得分:2)
C ++ 11模板语法对我来说非常陌生,但是像这样的递归问题最好使用显式OpenMP任务并行:
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT& f)
{
#pragma omp task firstprivate(I) shared(t,f)
{
f(std::get<I>(t));
}
for_each<I + 1, FuncT, Tp...>(t, f);
}
...
// Proper usage
#pragma omp parallel
{
#pragma omp single
for_each(...);
}
重要的一点是在for_each
区域内的single
构造中对parallel
进行顶级调用。因此,只有一个线程会调用for_each
,这反过来会导致f(std::get<I>(t));
排队等待以后作为显式任务执行。其他线程在single
结构末尾的隐式屏障处等待时,将开始从任务队列中拉出任务并并行执行它们,直到队列为空。为清楚起见,明确给出了任务使用的所有变量的共享类。
应该共享t
和f
引用的对象,并且引用本身(基本上是实现引用的指针)应该是firstprivate。另一方面,OpenMP标准禁止引用类型为firstprivate,不同的编译器供应商倾向于以不同方式实现该标准。英特尔C ++编译器接受以下代码,它在任务中提供正确的结果,但引用的变量是私有化的(这是错误的):
void f(int& p)
{
#pragma omp task
{
cout << "p = " << p << endl;
p = 3;
cout << "p' = " << p << endl;
}
}
void f1()
{
int i = 5;
#pragma omp parallel
{
#pragma omp single
f(i);
}
cout << "i = " << i << endl;
}
PGI的编译器给出了正确的结果,并没有私有化i
。另一方面,GCC正确地确定p
应该是firstprivate
但是然后遇到标准中的禁止并且给出编译时错误。
如果将任务修改为:
#pragma omp task shared(p)
{
...
}
它可以正常使用GCC,但任务打印错误的初始值p
,然后导致英特尔C ++编译器和PGI的C ++编译器出现分段错误。
去图!