【问题标题】:std::transform slower than for loopsstd::transform 比 for 循环慢
【发布时间】:2019-11-19 21:27:59
【问题描述】:

我曾考虑实现一个矩阵类,该类使用算法中的std::transform 进行计算,但我发现在某些情况下编写循环更快。

看看添加operator+= 以进行元素明智的添加。如果 rhs 矩阵有 1 col 而行数与 lhs 矩阵相同,我可以执行以下操作:

for (auto c = 0; c < cols(); ++c) {
    std::transform(std::execution::par, col_begin(c), col_end(c), rhs.begin(), col_begin(c), std::plus<>());
}

或使用简单的循环:

auto lhsval = begin();
auto rhsval= rhs.begin();

for (auto r = 0; r < rows(); ++r) {
   for (auto c = 0; c < cols(); ++c) {
       *lhsval += *rhsval;
       ++lhsval;
   }
   ++rhsval;
}

为了您的信息,我编写了一个接受步骤的迭代器。所以col_begin() 返回一个迭代器,它将跳过operator++ 中的其他列

我使用 google benchmark 计算了两种实现之间的差异,得出的结论是循环比使用 std::transform 快大约 5 倍。好吧,也许应该有区别,但没有那么大的区别。

您可以在my github repo查看完整代码

matrix class matrix iterator

【问题讨论】:

  • 如果没有std::execution::par,也会发生同样的事情吗?你是在发布模式下编译吗?
  • 提示:当询问性能(而不是效率)时,如果不指定如何编译代码,这些问题通常是无法回答的。我们需要 - 您的编译器、您的构建(调试/发布)和您的编译器选项(主要是优化标志)。
  • 恕我直言,您在这里遇到了缓存未命中。在第一个示例中,您在两个未连接的内存区域上迭代多次。在第二个中,您只迭代一个具有单个 rhs 值的区域,编译器可以轻松优化该区域
  • @bazz-dee 它一点也不大。缓存未命中(及其子类型,分支错误预测)可能会导致低级别的巨大性能问题。此外,我在您的代码中注意到您的矩阵是如何组织的。难怪在行上迭代然后在列上迭代更快 - 行中的单元格在内存中接近,在一个缓存行中。尝试反转 for 循环并检查性能。我很确定(如果编译器不优化它)它会几乎一样慢
  • 您的代码示例看起来并不相同。确保他们做同样的事情。并发布minimal, Complete, and Verifiable example

标签: c++ matrix c++17


【解决方案1】:

传递std::execution::par 是要求库并行化此操作。这会增加开销,即使只是为了确定“您的问题太小而无法并行化”。在并行化值得之前,被转换的元素数量必须非常大(有时数十万或数百万),并且需要您拥有适当的硬件(在两核机器上并行化比在64 核机器)。

for 循环版本更类似于没有std::execution::par 参数的普通std::transform。如果您删除该参数并且性能差异仍然很大,请使用该信息以及您的编译器版本、平台、编译器开关和有关您的数据集的信息更新您的问题:行数/列数等。

【讨论】:

  • 你好。测试数据是 1000x1000 矩阵。在 cmets 中,您可以看到其他信息。但我认为我只是有缓存未命中的评论是正确的。目前我发现了一个足够快的解决方案
猜你喜欢
  • 2020-08-04
  • 2021-07-28
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
  • 2013-06-02
  • 2017-11-09
  • 2019-12-08
相关资源
最近更新 更多