C++ OpenMP中两种方式使用蒙特卡罗方法计算pi

Div*_*tio 3 c++ algorithm openmp montecarlo

什么方法应该更快?第一种方法是增加一个变量以减少:

#pragma omp parallel private(seed, x, y, i) reduction (+:counter)
{
    seed = 25234 + 17 * omp_get_thread_num();
    nproc = omp_get_thread_num();
    #pragma omp parallel for
    for(i=0; i<prec/8; i++){
        x = (double)rand_r(&seed) / RAND_MAX;
                y = (double)rand_r(&seed) / RAND_MAX;
        if(x*x+y*y<1){
            counter++;
        } 

}
Run Code Online (Sandbox Code Playgroud)

第二个是使用每个进程的增量变量表,最后,该表中元素的总和是一个结果:

#pragma omp parallel private(seed, x, y, i , nproc)
{
    seed = 25234 + 17 * omp_get_thread_num();
    nproc = omp_get_thread_num();
    #pragma omp parallel for
    for(i=0; i<prec/8; i++){
        x = (double)rand_r(&seed) / RAND_MAX;
        y = (double)rand_r(&seed) / RAND_MAX;
        if(x*x+y*y<1){
            counter[nproc]++;
        } 

    }
}

double time = omp_get_wtime() - start_time;
int sum=0;
for(int i=0; i<8; i++){
    sum+=counter[i];

} 
Run Code Online (Sandbox Code Playgroud)

理论上,第二种方式应该更快,因为进程不是共享一个变量,而是每个进程都有自己的变量。但是当我计算执行时间时:

first approach: 3.72423 [s]

second approach: 8.94479[s]
Run Code Online (Sandbox Code Playgroud)

我认为错了还是我的代码做错了什么?

coi*_*oin 5

您是第二种方法中错误共享的受害者。这是来自英特尔的一篇有趣的文章

当不同处理器上的线程修改驻留在同一高速缓存行上的变量时,就会发生错误共享。这会使缓存行无效并强制进行内存更新以保持缓存一致性。

如果两个处理器对同一内存地址区域中的独立数据进行操作,这些数据可以存储在单行中,那么系统中的缓存一致性机制可能会强制整行跨总线或与每次数据写入互连,除了浪费系统带宽外,还迫使内存停顿

直觉上,我不认为第一种方法应该更慢。
您确实在每个线程上创建了一个私有副本,然后将最终结果应用到全局变量中。行为在某种程度上与您的共享数组相同,但这里的问题是即使您的访问是独立的,您也会得到错误的共享。