【发布时间】:2017-03-17 11:44:53
【问题描述】:
我正在开发一个复杂的框架,它使用std::function<> 作为许多函数的参数。通过分析,我发现了以下性能问题之一。
谁能解释一下为什么 Loop3a 这么慢?我预计将使用内联并且时间将相同。组装也是如此。有什么方法可以提高性能或不同的方式吗? C++17 会以这种方式做出任何改变吗?
#include <iostream>
#include <functional>
#include <chrono>
#include <cmath>
static const unsigned N = 300;
struct Loop3a
{
void impl()
{
sum = 0.0;
for (unsigned i = 1; i <= N; ++i) {
for (unsigned j = 1; j <= N; ++j) {
for (unsigned k = 1; k <= N; ++k) {
sum += fn(i, j, k);
}
}
}
}
std::function<double(double, double, double)> fn = [](double a, double b, double c) {
const auto subFn = [](double x, double y) { return x / (y+1); };
return sin(a) + log(subFn(b, c));
};
double sum;
};
struct Loop3b
{
void impl()
{
sum = 0.0;
for (unsigned i = 1; i <= N; ++i) {
for (unsigned j = 1; j <= N; ++j) {
for (unsigned k = 1; k <= N; ++k) {
sum += sin((double)i) + log((double)j / (k+1));
}
}
}
}
double sum;
};
int main()
{
using Clock = std::chrono::high_resolution_clock;
using TimePoint = std::chrono::time_point<Clock>;
TimePoint start, stop;
Loop3a a;
Loop3b b;
start = Clock::now();
a.impl();
stop = Clock::now();
std::cout << "A: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
std::cout << "ms\n";
start = Clock::now();
b.impl();
stop = Clock::now();
std::cout << "B: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
std::cout << "ms\n";
return a.sum == b.sum;
}
使用带有“-O2 -std=c++14”的 g++5.4 的示例输出:
A: 1794ms
B: 906ms
在分析器中,我可以看到许多这样的内部结构:
double&& std::forward<double>(std::remove_reference<double>::type&)
std::_Function_handler<double (double, double, double), Loop3a::fn::{lambda(double, double, double)#1}>::_M_invoke(std::_Any_data const&, double, double, double)
Loop3a::fn::{lambda(double, double, double)#1}* const& std::_Any_data::_M_access<Loop3a::fn::{lambda(double, double, double)#1}*>() const
【问题讨论】:
-
std::function 确实类型擦除并且从不内联。
-
恕我直言,在这种情况下,最好使用仿函数而不是 lambda。然后你可以有一个命名函数类型而不是
std::function,编译器擅长内联函子。 -
使用 lambda 代替
std::function是否工作得更快? IE。auto fn = [](double a, double b, double c) .... -
除了已经说过的其他内容之外:由于它不是 const,因此永远不能内联。您可以在运行时更改指针。因此编译器必须证明它从未被修改过才能内联它,这在某些情况下甚至是不可能的。
标签: c++ c++11 c++14 c++17 std-function