簡體   English   中英

Openmp 多線程代碼在使用多線程時給出不同的答案

[英]Openmp multithreaded code giving different answer when using multiple threads

我有以下帶有openmp多線程的蒙特卡羅代碼

int main()
{
  std::uniform_int_distribution<int> dir(0, 1);
  std::uniform_int_distribution<int> xyz(0, 2);
  std::uniform_real_distribution<double> angle(0,360);
  mt19937 gen{0};

  auto M = 20;
  long double sum = 0;
  auto num_trials = 10000;
  // omp_set_num_threads(12);

  #pragma omp parallel
  {
    double loc_sum = 0.0;
    #pragma omp for
    for(int i = 0; i < num_trials; ++i)
    {
      double x = 0;
      double y = 0;
      double z = 0;
      double r = 0; 
      auto N = 0;
      
      while(r < M)
      {
        auto d = dir(gen);
        auto p = xyz(gen);
        if(p == 0)
        {
          x += (d == 1) ? -1 : 1;
        }
        else if(p == 1)
        {
          y += (d == 1) ? -1 : 1;
        }
        else
        {
          z += (d == 1) ? -1 : 1;
        }

        r = std::sqrt(x * x + y * y + z * z);
        ++N;
      }

      loc_sum += N;
    }

    #pragma omp critical
      sum += loc_sum;
  }
}

串行執行和多線程執行的變量sum完全不同。 由於對隨機均勻分布的調用,我預計會有輕微的差異,但我觀察到的差異太大,不可能是由於隨機性,我懷疑我的多線程代碼中存在錯誤。

此代碼中是否存在影響sum競爭條件或數據競爭?

問題是您在調用生成器( dirxyz )時沒有鎖定它們。 您還可以在不鎖定的情況下使用 PRNG ( gen )。

這些調用都不是原子的,因為默認情況下實現它們會使單線程代碼比它需要的慢。

使用#pragma omp critical標記生成dp的行應該可以解決問題。

如果您不想要臨界區,則需要在每個線程中使用單獨的dirxyzgen對象。 生成器( dirxyz )可以簡單地復制。 PRNG ( gen ) 應該在每個線程中正確初始化,否則你最終會在每個線程中得到具有完全相同狀態的 PNG。 例如:

std::random_device rd; /* Outside the parallel section. */

// Code below once per thread.
/* Initialization of the PRNG calls std::random_device::operator() which 
 * needs a lock around it when called in parallel. */
std::mt199937 gen;    
#pragma omp critical
{
  gen.seed(rd());
}

// for-loop starts here.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM