gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-28 02:41:26 25 4
gpt4 key购买 nike

我正在尝试使用 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com