了解 std::function 的开销并捕获同步 lambda

问题描述 投票:0回答:1

考虑以下简单示例:

#include <QCoreApplication>
#include <QDebug>
#include <QElapsedTimer>
#include <QRandomGenerator>
#include <QDateTime>
#include <functional>

inline void reader(int * d, int c, std::function<void(int *)> foo) {
  while (c--) foo(d++);
}

template <class FOO>
inline void readerT(int * d, int c, FOO foo) {
  while (c--) foo(d++);
}

inline int summer(int * d, int c) {
  int s = 0;
  reader(d, c, [&s](int * dd){ s += *dd; });
  return s;
}

inline int summerT(int * d, int c) {
  int s = 0;
  readerT(d, c, [&s](int * dd){ s += *dd; });
  return s;
}

int main(int argc, char* argv[]) {
  QCoreApplication a(argc, argv);
  int tcount = 100000000;

  QVector<int> vec; vec.resize(tcount);
  QRandomGenerator gen = QRandomGenerator::securelySeeded();  
  gen.generate(vec.begin(), vec.end());

  QElapsedTimer t;
  t.restart();
  qDebug() << summer(vec.data(), tcount) << t.elapsed(); // 110 ms
  t.restart();
  qDebug() << summerT(vec.data(), tcount) << t.elapsed(); // 15 ms

  t.restart();
  int sum = 0;
  for (const auto & i : vec) { sum += i; }
  qDebug() << sum << t.elapsed(); // 15 ms

  return a.exec();
}

测试表明,非模板解决方案要慢得多,而模板解决方案似乎与常规循环一样快,或者零开销。

为什么编译器无法达到非模板版本相同的优化水平?

注释:使用GCC11 O3编译

c++ templates lambda inline std-function
1个回答
0
投票

问题归结为

std::function
的调用机制过于复杂,无法可靠地内联。 高效实施
std::function
的必要条件包括:

  • 小对象优化,以便较小的函数对象不会存储在堆上
  • 类型擦除,通常使用多态类
  • 函数指针的特殊情况,以避免过多的间接寻址

为了让编译器撤消所有这些事情,需要应用

  • 内联
  • 堆省略
  • 控制流分析/数据流分析

...非常完美。现代编译器在优化与函数指针有关的任何方面仍然很糟糕。 你只是期望太多了。

另一方面,

template
版本非常容易内联,如汇编输出所示(https://godbolt.org/z/vvG3fnT5f)。

© www.soinside.com 2019 - 2024. All rights reserved.