用于C++对象数组上循环的OpenMP

OpenMP for loop on array of C++ objects

本文关键字:循环 OpenMP 数组 C++ 对象 用于      更新时间:2023-10-16

我正在运行一个模拟,其中生成了许多随机数。RNG被实现为具有返回随机数的公共方法的C++对象。为了将它与OpenMP并行化一起使用,我只需创建一个这样的RNG对象数组,每个线程一个。然后,每个线程通过调用其中一个RNG来生成自己的随机数。例如:

for (int i = 0; i < iTotThreads; i++) {
aRNG[i] = new RNG();
}
// ... stuff here
#pragma omp parallel 
{
iT = omp_get_thread_num();
#pragma omp for
for ( /* big loop */) {
// more stuff
aRNG[iT]->getRandomNumber();
// more stuff
}
}  

尽管每个RNG都处理自己的成员变量,并且两个这样的RNG不适合在一个缓存行中(我在创建时也尝试过明确地对齐它们中的每一个),但似乎存在一些错误的共享,因为代码根本不可伸缩。

如果我实例化omp并行区域内的对象:

#pragma omp parallel
{ 
i = omp_get_thread_num();
aRNG[i] = new RNG();
}

代码的伸缩性非常好。你知道我在这里缺了什么吗?

编辑:顺便说一下,在第二种情况下(比例很好的情况),我创建RNG的平行区域与我使用它们的区域不同。当我进入第二个平行区域时,aRNG[]中的每个指针仍然会指向我的一个对象,但我想这是一种糟糕的做法。。。

尽管我从您的描述中怀疑错误共享是问题的原因,但为什么不以这种方式简化代码呢:

// ... stuff here
#pragma omp parallel 
{
RNG rng;
#pragma omp for
for ( /* big loop */) {
// more stuff
rng.getRandomNumber();
// more stuff
}
}

parallel区域内声明rng将是一个具有自动存储持续时间的私有变量,因此:

  • 每个线程都将有自己的私有随机数生成器(此处不允许错误共享)
  • 您不必管理资源的分配/解除分配

如果这种方法不可行,并且遵循@HristoIliev的建议,您可以始终声明一个threadprivate变量来保存指向随机数生成器的指针:

static std::shared_pointer<RNG> rng;
#pragma omp threadprivate(rng);

并将其分配到第一个并行区域:

rng.reset( new RNG );

在这种情况下,尽管有一些注意事项可以确保rng的值在平行区域中保持不变(引用OpenMP 4.0标准):

非初始的threadprivate变量中的数据值线程保证在两个连续的活动线程之间持久存在平行区域,仅当以下所有条件都成立时:

  • 两个平行区域都没有嵌套在另一个显式平行区域中
  • 用于执行两个并行区域的线程数相同
  • 用于执行两个并行区域的线程关联策略是相同的
  • 封闭任务区域中的dyn-var内部控制变量的值在进入两个平行区域时都为false

如果这些条件都成立,并且如果threadprivate变量在两个区域中引用,然后使用相同线程号的线程在各自的地区将引用相同的副本变量