【问题标题】:Why does sorting a vector with execution::par take longer than normal sort (gcc 10.1.0)?为什么使用 execution::par 对向量进行排序比正常排序(gcc 10.1.0)花费更长的时间?
【发布时间】:2020-11-04 02:14:46
【问题描述】:

考虑这段代码:

#include <algorithm>
#include <chrono>
#include <cstdio>
#include <execution>
#include <functional>
#include <random>
#include <vector>
using namespace std;
using namespace std::chrono;

constexpr size_t NUM_OF_ELEMENTS = 30000000;

// execute lambda and print the execution time
void measure(function<void()> lambda)
{
    auto start = high_resolution_clock::now();
    lambda();
    auto end = high_resolution_clock::now();
    
    printf("%ld\n", duration_cast<microseconds>(end - start).count());
}

int main()
{
    random_device rd;
    mt19937_64 gen(rd());
    // range from INT_MIN to INT_MAX
    uniform_int_distribution<> distr(-2147483648, 2147483647);
    
    vector<int> original;
    original.reserve(NUM_OF_ELEMENTS);
    
    for(size_t i = 0; i < NUM_OF_ELEMENTS; i++)
        original.push_back(distr(gen));
    
    vector<int> the_copy(original.begin(), original.end());
    
    // sort with single thread
    measure([&]{ sort(original.begin(), original.end()); });
    
    // sort with execution::par
    measure([&]{ sort(execution::par, the_copy.begin(), the_copy.end()); });
    return 0;
}

代码可以总结为几点:

  • 创建随机数生成器
  • 创建随机整数向量
  • 创建该向量的副本
  • 用一个线程对原始向量进行排序并测量执行时间
  • 使用std::execution::par对副本进行排序并测量执行时间
  • 打印执行时间

execution::par 版本总是需要更长的时间。不管NUM_OF_ELEMENTS 有什么价值。我尝试了从 100 000 到 30 000 000 以 100 000 递增的值。上面的代码会产生类似的结果(以微秒为单位的值):

9729406    // single thread
10834613   // execution::par

我使用 VS Code 在 Windows 10 上使用 gcc 编译了代码: g++ -std=c++17 -g ${workspaceFolder}/main.cpp -o ${workspaceFolder}/main.exe

对于 C++ 标准库,我使用 here 的 mingw 发行版。 程序版本:GCC 10.1.0 + LLVM/Clang/LLD/LLDB 10.0.0 + MinGW-w64 7.0.0

我的处理器有 6 个内核,在执行时我没有运行任何主要程序或后台进程。

首先,我认为这与向量的大小有关,但 30 000 000 个元素肯定足够了。在完成单次测试之前,它已经运行了 10 秒。

  • 这是怎么回事?
  • execution::par 是不是应该这样使用?
  • 是否必须启用某些编译标志或其他技巧才能使其按预期工作?

【问题讨论】:

  • 您不应该对未优化的代码进行性能比较。使用-O2,您的性能结果可能会发生巨大变化。
  • 如果交换 sort 和 par sort 调用的行会产生什么结果?
  • @JorgeBellon 遗憾的是,事实并非如此。使用-O2,它为两者提供了更好的时间:2111474 and 2172487execution::par 仍然较慢。
  • 在一个问题上投入更多线程并不能保证更快的代码。尤其是你使用的线程越多,任务切换和操作系统必须执行的任务就越多,如果你不小心,这实际上会减慢代码。
  • @Taekahn 我同意。但这是基本的排序。多线程应该会更快。

标签: c++ multithreading sorting c++17 mingw-w64


【解决方案1】:

在您的代码上运行 perf,看起来它花费了一点点时间来尝试对数据进行分区。

这只是一个示例,但我运行了几次,并且始终并行版本需要更长的时间才能在多个排序级别上对数据进行分区。由于它是递归的,因此很难准确了解它最终总共增加了多少额外开销。

sort1 是非并行排序。
sort2 是并行排序。

除此之外,基本问题的答案是,您需要安装英特尔线程构建块,以便 gcc 使用串行算法以外的任何东西。
这可以通过sudo apt install libtbb-dev 在Linux 上快速安装,然后使用-ltbb 链接它

【讨论】:

  • 你确定多线程排序实际上使用了多个线程?
  • @Mikhail 不,我只能说它正在调用并行版本,而不是并行运行。无论如何,问题和答案中发布的代码,至少在我的机器上,关于为什么需要运行更长时间的原因是因为在我测试的每次运行时对数据进行分区需要更长的时间。
  • @Mikhail 挖掘 GCC 的源代码,看起来只有在安装了线程构建块的情况下才会选择并行执行。 github.com/gcc-mirror/gcc/blob/…
  • @Taekahn 我认为您刚刚发布的评论是这个问题的正确答案。它没有并行运行,因为我没有安装 TBB。顺便说一句,您知道如何安装它的一些简单方法吗?我搜索过,有人遇到问题:example issue.
  • 我会将其添加到答案中。至于安装它,在最近的 linux 上,它超级简单。 sudo apt install libtbb-dev 然后你只需链接它。在窗户上?我真的无法提供任何指导。
猜你喜欢
  • 2015-09-13
  • 1970-01-01
  • 2018-04-02
  • 2018-08-30
  • 2021-01-21
  • 2012-08-23
  • 1970-01-01
  • 2023-03-17
  • 2017-05-01
相关资源
最近更新 更多