c++ - 为什么具有随机数生成功能的 OpenMP 比串行代码慢

标签 c++ random openmp

我正在尝试使用 OpenMP 为我的程序添加并行性。

std::random_device rd;
std::mt19937 generator(rd());
std::uniform_real_distribution<float> distribution(-0.5, 0.5);

#pragma omp parallel for
for(int i = 0; i < 100000000; i++)
{
    float x = distribution(generator);
}

我在 windows(Visual Studio 2010)和 linux(Centos 6.5,gcc 4.9.1)的 12 核处理器上测试了代码,发现并行版本比串行代码慢。

结果如下:

g++ test.cpp -o test -std=c++11 -Ofast
time ./test
real    0m1.234s
user    0m1.229s
sys 0m0.004s

g++ test.cpp -o test -fopenmp -std=c++11 -Ofast
time ./test
real    0m1.708s
user    0m24.218s
sys 0m0.010s

为什么 OpenMP 比串行代码慢?

最佳答案

您正在多个线程中使用一个随机数生成器。每次调用新随机数都会阻塞所有其他并行调用,直到完成。

如果您分析代码,很可能所有(或大部分)执行时间都花在了某种形式的互斥锁锁定/解锁上。这个问题叫做contention并且您的场景将是如何导致它的教科书示例。

如果您使用 std::thread 并为每个线程提供一个单独的 rng,您将对该部分代码实现几乎 100% 的并行化。

下面的一些代码可以让您开始使用 std::thread。注意 std::ref

的使用
#include <array>
  using std::array;
#include <cstddef>
  using std::size_t;
#include <functional>
  using std::ref;
#include <iostream>
  using std::cout;
#include <iterator>
  using std::iterator_traits;
#include <thread>
  using std::thread;
#include <vector>
  using std::vector;
#include <random>
  using mersenne_twister = std::mt19937;

template<class T, T N>
array<T, N> series_of_numbers()
{
  array<T, N> arr;
  for(T i=0; i<N; ++i)
    arr[i] = i;

  return arr;
}

template<class Iterator, class Engine>
void generate_rng(Iterator begin, Iterator end, Engine& engine)
{
  std::uniform_real_distribution<double> dist;
  for(auto it = begin; it != end; ++it)
    *it = dist(engine);
}

int main()
{
  const size_t amount_of_random_numbers = 1024;
  // Engines
  const size_t Nrng = 4;
  auto seed_values = series_of_numbers<size_t, Nrng>(); // choose other seeds if you wish
  array<mersenne_twister, Nrng> engines;
  for(size_t i=0; i<Nrng; ++i)
    engines[i].seed(seed_values[i]);

  vector<thread> threads;
  vector<double> rngs(amount_of_random_numbers);

  // relevant iterators with offsets
  vector<vector<double>::iterator> begins = { rngs.begin(),
                                              rngs.begin() + amount_of_random_numbers/Nrng,
                                              rngs.begin() + 2*amount_of_random_numbers/Nrng,
                                              rngs.begin() + 3*amount_of_random_numbers/Nrng };

  vector<vector<double>::iterator> ends = { rngs.end(),
                                              rngs.end() - 3*amount_of_random_numbers/Nrng,
                                              rngs.end() - 2*amount_of_random_numbers/Nrng,
                                              rngs.end() - amount_of_random_numbers/Nrng };
  // create threads
  for(size_t n=0; n<Nrng; ++n)
    threads.emplace_back(thread(generate_rng<decltype(begins[n]), mersenne_twister>, ref(begins[n]), ref(ends[n]), ref(engines[n])));

  // join threads -> this is where the work will be done.
  for(size_t n=0; n<Nrng; ++n)
    threads[n].join();

  // rngs is filled with magical values!
  for(auto number : rngs)
    std::cout << number << '\n';
}

Live demo at Coliru .和 another version您实际上可以将线程数更改为 4 的任意倍数

关于c++ - 为什么具有随机数生成功能的 OpenMP 比串行代码慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25847164/

相关文章:

windows - 跨线程的数值差异(cygwin 上的 openMP)

C++:错误:获取临时 [-fpermissive] 的地址

c++ - 向旧的 C++ 意大利式编码器介绍 MVC?

c++ - 使用 Visual Studios 2012 进行 C++ 编码时,如何自动完成在 IntelliSense 中选择的代码?

python - 如何在 Python 3 中生成 0 到无穷大之间的 N 个随机数

python - 如何有效地生成一个只包含 Python 中唯一数字的随机数?

java - 如何在每次调用方法时获得大量的随机整数?

fortran - OpenMP 工作精度

C++哪个容器用于存储缓存内存

c++ - 有什么方法可以强制openmp获得所需的线程数?