并行计算大矢量的总和

时间:2015-01-20 15:01:06

标签: c++ multithreading algorithm parallel-processing boost-asio

问题背景

我有一个程序目前花费的时间太长,无法使用std::vector总结大约{1}个约1亿个元素,这是一个瓶颈。

我希望它更快,我希望它是一个异步计算,因此GUI / Server不会阻止。计算也应该使用多线程,这样我就可以减少总结向量所需的时间。

我想分割求和,以便每个线程对向量的一部分求和,然后在计算所有部分求和时,应将每个线程的部分求和加在一起得到总和。

Boost.Asio的?

我想知道如何在 Boost.Asio 中解决这个问题?理想情况下,我的程序需要重用线程(如线程组),不确定如何存储和检索部分和,最后检索部分和的总和。

我正在考虑创建一个调用std::accumulate的线程组,传递一个处理程序来计算部分和,但我不确定如何将部分和传递给另一个处理程序并将所有部分和加在一起

如果有人展示了我可以解决这个问题的一些骷髅代码,那将会很棒。

2 个答案:

答案 0 :(得分:4)

Boost.Asio适合这个问题吗?

Boost.Asio的主要目的是为网络 I / O编程提供异步模型,你描述的问题似乎没什么用做网络和I / O.

我认为最简单的解决方案是使用Boost或C ++标准库提供的线程原语

并行算法

以下是仅使用标准库创建的accumulate并行版本的示例。

/* Minimum number of elements for multithreaded algorithm.
   Less than this and the algorithm is executed on single thread. */
static const int MT_MIN_SIZE = 10000;

template <typename InputIt, typename T>
auto parallel_accumulate(InputIt first, InputIt last, T init) {
    // Determine total size.
    const auto size = std::distance(first, last);
    // Determine how many parts the work shall be split into.
    const auto parts = (size < MT_MIN_SIZE)? 1 : std::thread::hardware_concurrency();

    std::vector<std::future<T>> futures;

    // For each part, calculate size and run accumulate on a separate thread.
    for (std::size_t i = 0; i != parts; ++i) {
        const auto part_size = (size * i + size) / parts - (size * i) / parts;
        futures.emplace_back(std::async(std::launch::async,
            [=] { return std::accumulate(first, std::next(first, part_size), T{}); }));
        std::advance(first, part_size);
    }

    // Wait for all threads to finish execution and accumulate results.
    return std::accumulate(std::begin(futures), std::end(futures), init,
        [] (const T prev, auto& future) { return prev + future.get(); });
}

Live example (并行版本与Coliru上的顺序执行大致相同,可能只有1个核心可用)

计时

在我的机器上(使用8个线程),并行版本平均可以提升约120%的性能。

  

顺序总和:
  所用时间:46毫秒
  50000000.5亿个
  --------------------------------
  平行和:
  所用时间:21毫秒
  50000000.5亿个

然而,100,000,000个元素的绝对增益仅是微不足道的(25毫秒)。虽然,在累积与int不同的元素类型时,性能增益可能会更大。

OpenMP的

正如@sehe在评论中所提到的,值得一提的是 OpenMP 可能会为这个问题提供一个简单的解决方案,例如。

template <typename T, typename U>
auto omp_accumulate(const std::vector<T>& v, U init) {
    U sum = init;

    #pragma omp parallel for reduction(+:sum)
    for(std::size_t i = 0; i < v.size(); i++) {
        sum += v[i];
    }

    return sum;
}

在我的机器上,此方法的执行方式与使用标准线程基元的并行方法相同。

  

顺序总和:
  所用时间:46毫秒
  50000000.5亿个
  --------------------------------
  平行和:
  所用时间:21毫秒
  总和:5000000050000000
  --------------------------------
  OpenMP总和:
  所用时间:21毫秒
  总和:5000000050000000

答案 1 :(得分:1)

可以使用Boost Asio作为线程池。但除非你有......异步IO操作来协调,否则它没有多大意义。

在回答&#34; c++ work queues with blocking &#34;我展示了两个thread_pool实现:

  • 解决方案#1:基于boost::asio::io_service
  • 的解决方案
  • 解决方案#2:另一个基于boost::thread原语

两者都接受任何void()签名兼容任务。这意味着,你可以将你的函数包裹起来 - 将重要结果返回到packaged_task<...>并从中获取future<RetVal>