考虑以下简单示例:
#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编译
问题归结为
std::function
的调用机制过于复杂,无法可靠地内联。
高效实施 std::function
的必要条件包括:
为了让编译器撤消所有这些事情,需要应用
...非常完美。现代编译器在优化与函数指针有关的任何方面仍然很糟糕。 你只是期望太多了。
另一方面,
template
版本非常容易内联,如汇编输出所示(https://godbolt.org/z/vvG3fnT5f)。