2

我一直无法让线程化 C++ 随机数生成器在单核上优于标准 rand()。(我也看到多个线程调用 rand() 的问题)

我知道某处存在并发问题,但我看不到。我已经到了下面,我知道 openmp 正在拆分 8 个可用内核。当我注释掉 omp 行时,我会得到更快的结果。请帮忙!这真让我抓狂!

时代

Single thread
time ./a.out 
real    0m3.497s
user    0m3.492s
sys 0m0.000s

OpemMP 8 cores
g++ -fopenmp randtests.cpp 
time ./a.out 
real    0m14.723s
user    1m52.275s
sys 0m0.712s

编码:

#include <omp.h>        
#include "boost/random.hpp"
#include "boost/generator_iterator.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include "boost/random.hpp"

using namespace std;

class RNG
{
public:
    typedef boost::random::mt19937 Engine;
    typedef boost::random::uniform_smallint<int> Distribution;

    Engine engine;
    Distribution distributer;

    RNG() : engine(), distributer() {
        engine.seed();     }

    int operator()() {
        return distributer(engine);
    }
};


int main(void) {

#pragma omp parallel
    {
        int i = omp_get_thread_num();
        unsigned int myseed = i;
        RNG  r;
        int y;
#pragma omp for ordered schedule(dynamic) nowait
        for (unsigned int x = 0; x < 100000000; x++) {
            y = r();
        }
    }

return 0;
}
4

2 回答 2

3

您的代码执行如此缓慢有两个原因。

首先,你有schedule(dynamic)条款。这使得您的循环的调度是动态的,即每次迭代都以先到先得的方式调度到一个单独的线程。这是非常低效的。改为schedule(static)让每个线程执行预先计算的迭代范围。均匀分布在恒定时间内生成,因此每次迭代将花费相同的时间,因此无需使用动态调度。

其次,您已指示编译器创建一个有序的并行循环。由于涉及同步,排序会产生非常高的额外开销。在您的情况下,开销更大,因为调度是动态的。

我真的不明白为什么在这种情况下你有动态的时间表和排序。只需将 OpenMP 编译指示替换为:

#pragma omp for schedule(static)

(该nowait条款在这种情况下也只有表面的影响)

于 2013-04-24T22:31:47.993 回答
1

我相当确信线程正在竞争共享资源,即state随机数生成器中的共享资源。对于您获取的每个数字,state都会更新(下次提供另一个数字)。由于所有随机数只有一种状态,并且需要在不受其他线程干扰的情况下进行更新,因此它将强制线程按顺序运行(并且因为当您有 8 个线程调用相同的函数时,锁会严重竞争,从而迫使另一个线程等待,这可能意味着需要更多时间调用操作系统)。

另一个因素很可能是缓存共享——它state被所有线程修改,因此每次r()调用时都必须使保存状态的缓存行失效。

. 您可以通过将您的电话替换为distributer(engine)类似的来证明这一点static int x; return x++;。如果在多进程环境中性能更差(比每个线程数线性好),那么缓存共享就是问题的一部分。

于 2013-04-24T22:14:07.090 回答