我正在使用while循环来启动几个omp任务。每个任务都需要复制一个相当大的对象(作为第一个私有对象)。由于我的设置,大对象(在此示例中为矢量)将被天真地复制两次:
struct bigStruct {
bool next() {
/* do something with m_bigVector */
}
std::vector<int> m_bigVector;
/* other (big) data members */
};
bigStruct s;
#pragma omp parallel
{
#pragma omp single
while (s.next()) {
auto obj = s.m_bigVector; //copy the first time
#pragma omp task firstprivate(obj) //copy the second time
{
/* do something with obj */
}
}
} //end parallel
Run Code Online (Sandbox Code Playgroud)
gcc优化(-O3)似乎并没有以任何方式优化两个复制步骤。一个(不太优雅的)解决方案是使用显式的new/delete:
#pragma omp parallel
{
#pragma omp single
while (s.next()) {
auto obj_ptr = new std::vector<int>(s.m_bigVector); //copy once
#pragma omp task firstprivate(obj_ptr) //copy only the pointer
{
/* do something with obj */
delete obj_ptr;
}
}
} //end parallel
Run Code Online (Sandbox Code Playgroud)
还有modern/elegant解决这个问题的方法吗?也许是一种告诉任务移动对象而不是复制对象的方法?请注意,我不想复制整体,bigStruct因为它包含其他大数据成员。
好消息!
\n\n\n\n\nFirstPrivate 变量不得具有引用类型。
\n
自 OpenMP 4.5 (2015) 起已过时。目前没有这样的限制。有一个要求:
\n\n\n\n\n如果工作共享构造上的子句中的列表项
\nfirstprivate具有引用类型,则它必须绑定到团队的所有线程的同一对象。
但这并不适用 - 该task构造不是工作共享构造,并且无论如何都不会被多个线程遇到。
要充分了解该标准的要求:
\n\n(关于清单项目私有化)
\n\n\n\n\n如果列表项的类型是对类型的引用
\n\nT,则该类型将被视为用于T本子句的所有目的。为构造分配具有自动存储持续时间的相同类型的新列表项。\n 这些列表项的存储和生命周期将持续到创建它们的块退出为止。
\n\n对于类类型的每个变量:
\n\n\xe2\x80\xa2 如果该
\nfirstprivate子句不在target构造上,则调用复制构造函数来执行初始化
所以您可以安全地执行以下操作:
\n\nauto& obj = s.m_bigVector;\n#pragma omp task firstprivate(obj) // call copy ctor once\nRun Code Online (Sandbox Code Playgroud)\n\n不幸的是你不能
\n\nconst auto&是因为这样 的类型obj将是 const,因为仅删除了引用。firstprivate声明中。这很好,但仅适用于只有单个线程实际遇到数据共享子句的任务。