与使用模板函数相比,使用“function”对象的代码的执行速度

Gio*_*gre 0 c++ templates functional-programming function-object c++11

我知道这std::function是用类型擦除惯用法实现的。类型擦除是一种方便的技术,但它的缺点是它需要在堆上存储底层对象的寄存器(某种数组)。

因此,当创建或复制function对象时,需要进行分配,因此该过程应该比简单地将函数作为模板类型进行操作要慢。

为了检查这个假设,我运行了一个测试函数,该函数累积n = cycles连续的整数,然后将总和除以增量数n。首先编码为模板:

#include <iostream>
#include <functional>
#include <chrono>
using std::cout;
using std::function;
using std::chrono::system_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;

double computeMean(const double start, const int cycles) {
    double tmp(start);
    for (int i = 0; i < cycles; ++i) {
        tmp += i;
    }
    return tmp / cycles;
}

template<class T>
double operate(const double a, const int b, T myFunc) {
    return myFunc(a, b);
}
Run Code Online (Sandbox Code Playgroud)

main.cpp

int main()
{
    double init(1), result;
    int increments(1E9);
    // start clock
    system_clock::time_point t1 = system_clock::now();

    result = operate(init, increments, computeMean);
    // stop clock
    system_clock::time_point t2 = system_clock::now();

    cout << "Input: " << init << ", " << increments << ", Output: " << result << '\n';
    cout << "Time elapsed: " << duration_cast<milliseconds>(t2 - t1).count() << " ms\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

该程序运行了一百次,得到的平均结果为10024.9 ms

function然后我在 中引入该对象main,并加上一个模板专门化,operate以便上面的代码可以被回收:

// as above, just add the template specialization
template<>
double operate(const double a, const int b, function<double (const double, const int)> myFunc) {
    cout << "nontemplate called\n";
    return myFunc(a, b);
}

// and inside the main
int main()
{
    //...
    // start clock
    system_clock::time_point t1 = system_clock::now();

    // new lines
    function<double (const double, const int)> computeMean =
        [](const double init, const int increments) {
            double tmp(init);
            for (int i = 0; i < increments; ++i) {
                tmp += i;
            }
            return tmp / increments;
        };
    // rest as before
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我预计该function版本会更快,但平均值大致相同,实际上甚至更慢result = 9820.3 ms。检查了标准差,它们大致1233.77相同1234.96

这有什么意义呢?我预计对象的第二个版本function会比模板版本慢。

这里整个测试可以在GDB上运行。

G. *_*pen 5

我知道这std::function是用类型擦除惯用法实现的。类型擦除是一种方便的技术,但它的缺点是它需要在堆上存储底层对象的寄存器(某种数组)。

类型擦除不一定需要堆分配。在这种情况下, 的实现很可能std::function不必进行任何堆分配,因为 lambda 不捕获任何变量。因此,std::function只需存储函数指针,它将在对象本身中执行此操作,而不是在堆分配的内存中。

除此之外,即使确实std::function进行了堆分配,某些编译器甚至可能会忽略这些堆分配

最后但并非最不重要的一点是,虽然堆分配比堆栈分配更昂贵,但如果您只需在程序的整个持续时间内在堆上分配一次某些内容,您可能不会注意到由于该分配而导致的时间上的任何差异。

  • GCC 的 std::function 实现不需要为最大 16 字节大小的可调用对象(在 x86-64 上)进行堆分配,而是使用内部预分配的缓冲区。[关于如何在 GCC 中实现的粗略草图](/sf/ask/5055042051/#72215967) (2认同)
  • @Giogre,更准确地说它*可以*一样快。无法保证情况总是如此。 (2认同)