c ++为什么std :: async比顺序执行慢

时间:2017-11-08 15:35:32

标签: c++ multithreading c++11 asynchronous

#include <future>
#include <iostream>
#include <vector>
#include <cstdint>
#include <algorithm>
#include <random>
#include <chrono>
#include <utility>
#include <type_traits>

template <class Clock = std::chrono::high_resolution_clock, class Task>
double timing(Task&& t, typename std::result_of<Task()>::type* r = nullptr)
{
  using namespace std::chrono;
  auto begin = Clock::now();
  if (r != nullptr) *r = std::forward<Task>(t)();
  auto end = Clock::now();
  return duration_cast<duration<double>>(end - begin).count();
}

template <typename Num>
double sum(const std::vector<Num>& v, const std::size_t l, const std::size_t h)
{
  double s;
  for (auto i = l; i <= h; i++) s += v[i];
  return s;
}

template <typename Num>
double asum(const std::vector<Num>& v, const std::size_t l, const std::size_t h)
{
  auto m = (l + h) / 2;
  auto s1 = std::async(std::launch::async, sum<Num>, v, l, m);
  auto s2 = std::async(std::launch::async, sum<Num>, v, m+1, h);
  return s1.get() + s2.get();
}

int main()
{
  std::vector<uint> v(1000);
  auto s = std::chrono::system_clock::now().time_since_epoch().count();
  std::generate(v.begin(), v.end(), std::minstd_rand0(s));

  double r;
  std::cout << 1000 * timing([&]() -> double { return asum(v, 0, v.size() - 1); }, &r) << " msec | rst " << r << std::endl;
  std::cout << 1000 * timing([&]() -> double { return sum(v, 0, v.size() - 1); }, &r) << " msec | rst " << r << std::endl;
}

您好,

以上是用于求和随机数向量的两个函数。

我做了好几次,但似乎我没有从std::async中受益。以下是我得到的一些结果。

0.130582 msec | rst 1.09015e+12
0.001402 msec | rst 1.09015e+12

0.23185 msec | rst 1.07046e+12
0.002308 msec | rst 1.07046e+12

0.18052 msec | rst 1.07449e+12
0.00244 msec | rst 1.07449e+12

0.190455 msec | rst 1.08319e+12
0.002315 msec | rst 1.08319e+12

异步版本的所有四种情况都花费了更多时间。但理想情况下我应该快两倍吧?

我的代码中是否遗漏了任何内容?

顺便说一下,我在OS X 10.10.4 Macbook Air上使用1.4 GHz Intel Core i5

谢谢,

编辑:

  1. 编译器标志:g++ -o asum asum.cpp -std=c++11
  2. 我将标记更改为包含-O3,矢量大小为10000000,但结果仍然是必需的。
  3. 72.1743 msec | rst 1.07349e+16
    14.3739 msec | rst 1.07349e+16
    
    58.3542 msec | rst 1.07372e+16
    12.1143 msec | rst 1.07372e+16
    
    57.1576 msec | rst 1.07371e+16
    11.9332 msec | rst 1.07371e+16
    
    59.9104 msec | rst 1.07395e+16
    11.9923 msec | rst 1.07395e+16
    
    64.032 msec | rst 1.07371e+16
    12.0929 msec | rst 1.07371e+16
    

3 个答案:

答案 0 :(得分:6)

这里

auto s1 = std::async(std::launch::async, sum<Num>, v, l, m);
auto s2 = std::async(std::launch::async, sum<Num>, v, m+1, h);

async将存储自己的矢量副本两次。您应该使用std::cref并确保在向量消失之前检索期货(就像在当前代码中一样)并且访问得到正确同步(就像在当前代码中一样)。

正如评论中所提到的,线程创建开销可能会进一步降低代码速度。

答案 1 :(得分:1)

嗯,这是最简单的例子,由于以下原因,结果不应该具有约束力。

  1. 创建线程时,需要一些额外的CPU周期来创建线程上下文和堆栈。这些周期被添加到求和函数中。

  2. 当主线程运行此代码时,主线程为空,除了执行总和之外没有做任何其他事情

  3. 只有当我们无法在单个线程中完成某些事情或者我们需要同步等待某些输入时,才会使用多线程解决方案。

    仅仅通过创建线程,您就不会提高性能。通过仔细设计多线程应用程序来提高性能,在其他一些东西等待IO IO时可以使用空CPU#

答案 2 :(得分:1)

首先,与顺序功能相比,原始异步功能的性能很差,因为它会在其他答案中提到一个测试数据的副本。其次,在修复复制问题后,您可能无法看到改进,因为创建线程并不便宜,并且可能会损害您的性能提升。

根据基准测试结果,我可以看到异步版本比N = 1000000的顺序版本快1.88倍。但是,如果我使用N = 10000,则异步版本慢3.55倍。非迭代器和迭代器解决方案都产生类似的结果。

除此之外,你应该在编写代码时使用迭代器,因为这种方法更灵活,例如你可以尝试不同的容器类型,与C风格版本相比会给你类似的性能,而且它也更优雅恕我直言:)。 / p>

基准测试结果:

.Count()

完整的代码示例

complexData