5

I'm getting a "bus error" from an OpenMP parallel section of code. I recreated a simple version of my problem below. The code essentially makes many calls to the function uniform_distribution, which draws an integer between 0 and 20000 using Boost's uniform_int_distribution.

This post warns of two threads accessing the same object. I'm guessing that's eng in my case. (Unfortunately I don't know how to write "an appropriate mutex wrapper", as that post suggests).

A possible dirty solution I thought of was to create a local eng inside the #pragma for loop and to pass that as an argument to uniform_distribution. I don't like this idea because in my real code, I'm calling many functions, and passing a local eng would be cumbersome. Also, my concern is that different threads will generate the same random number sequence if I declare eng inside uniform_distribution. So I have two requirements: How do I parallelize in a way that

  1. Each thread is generating probabilistically independent draws from other threads?
  2. No race conditions occur on the RNG?

Thanks; any help is warmly appreciated.

#include <omp.h>
#include <boost/random/uniform_int_distribution.hpp>

boost::random::mt19937  eng;

int uniform_distribution(int rangeLow, int rangeHigh) {
    boost::random::uniform_int_distribution<int> unirv(rangeLow, rangeHigh);
    return unirv(eng);
}
int main()
{  
    # pragma omp parallel for private(eng)
    for (int bb=0; bb<10000; bb++)
        for (int i=0; i<20000; i++)
             int a = uniform_distribution(0,20000);

    return 0;
}
4

3 回答 3

3

当您并行化某些代码时,您必须考虑共享资源,这可能会导致数据竞争,进而最终可能会破坏您的程序。(注意:并非所有数据竞争都会破坏您的程序。)

在您的情况下,正如您正确预期的那样,eng是由两个或多个线程共享,必须避免正确执行。

您的情况的解决方案是私有化:为共享资源制作每个线程的副本。您需要创建一个单独的eng.

有很多方法可以进行私有化eng

(1) 尽量使用threadprivate指令(链接):例如,#pragma omp threadprivate(eng). 但是,某些编译器可能不支持该指令的非 POD 结构。

(2) 在 wherethreadprivate不可用的情况下,使用eng线程 id 的数组和访问:声明如eng[MAX_THREAD]. 然后,使用线程 id: 访问eng[omp_get_thread()]

但是,第二种解决方案需要考虑虚假共享,这会严重损害性能。最好保证其中的每个项目eng[MAX_THREAD]都分配在单独的缓存行边界上,在现代台式机 CPU 中通常为 64 字节。还有几种方法可以避免虚假共享。最简单的解决方案是使用填充:例如,char padding[x]在一个struct包含eng.

于 2013-03-19T17:21:19.230 回答
3

我认为最方便的解决方案将涉及一个thread_localRNG 和一个将线程 ID 作为每个线程的唯一编号的播种,例如,您可以在系统时间和线程 ID 之间进行 XOR 来播种 RNG。类似于(使用C ++ 11)的东西:

#include <omp.h>
#include <boost/random/uniform_int_distribution.hpp>

#include <thread>
#include <ctime>

boost::random::mt19937& get_rng_engine() {
  thread_local boost::random::mt19937 eng(
    reinterpret_cast<unsigned int>(std::time(NULL)) ^ std::this_thread::get_id());
  return eng;
};

(注意:<random>如果要使用 C++11,也可以使用)

如果您不能使用 C++11,那么您可以使用boost::thread类似的行为来代替,请参阅线程本地存储上的 Boost 页面。

于 2013-03-19T17:26:01.367 回答
0

你有两个选择:

  • 每个线程都有单独的随机数生成器并以不同的方式播种
  • 使用互斥

先来个互斥的例子:

# pragma omp parallel for
for (int bb=0; bb<10000; bb++)
{
    for (int i=0; i<20000; i++)
    {
        // enter critical region, disallowing simulatneous access to eng
        #pragma omp critical
        {
            int a = uniform_distribution(0,20000);
        }
        // presumably some more code...
    }
    // presumably some more code...
}

接下来,一个带有种子的线程本地存储示例:

# pragma omp parallel
{
    // declare and seed thread-specific generator
    boost::random::mt19937 eng(omp_get_thread_num());
    #pragma omp for
    for (int bb=0; bb<10000; bb++)
    {
        for (int i=0; i<20000; i++)
        {
            int a = uniform_distribution(0,20000, eng);
            // presumably some more code...
        }
        // presumably some more code...
    }
}

这两个片段都只是说明性的,根据您的要求(例如安全相关、游戏与建模),您可能希望选择其中一个。您可能还想更改确切的实现以适合您的使用。例如,如果您希望生成器可重复或更接近真正随机(是否可能是系统特定的),那么如何播种生成器很重要。这同样适用于两种解决方案(尽管在互斥情况下要获得可重复性更难)。

线程本地生成器可能运行得更快,而互斥情况应该使用更少的内存。

编辑:需要明确的是,互斥解决方案只有在随机数的生成不是线程工作的大部分时才有意义(即// presumably some more code...在示例中存在并且不需要花费大量时间来完成)。关键部分只需要包含对共享变量的访问,稍微改变你的架构可以让你更好地控制它(在线程本地存储的情况下,也可以让你避免传递eng引用)

于 2013-03-19T17:23:52.257 回答