std :: function的性能开销是多少?

61 c++ boost std

我在论坛上听到使用std::function<>原因性能下降.这是真的吗?如果是真的,这是一个很大的性能下降?

Cas*_*eri 77

实际上,std:function在使用它时必须考虑到性能问题.std::function它的类型擦除机制的主要优势并非免费,我们可能(但不一定必须)为此付出代价.

std::function是一个包装可调用类型的模板类.但是,它不是在可调用类型本身上进行参数化,而是仅在其返回和参数类型上进行参数化.可调用类型仅在构造时知道,因此,std::function不能具有此类型的预先声明的成员来保存给予其构造函数的对象的副本.

粗略地说(实际上,事情比这更复杂)std::function只能保存一个指向传递给它的构造函数的对象的指针,这就引发了一个生命周期问题.如果指针指向生命周期小于对象生命周期的std::function对象,则内部指针将变为悬空状态.要防止此问题,std::function可以通过调用operator new(或自定义分配器)在堆上创建对象的副本.动态内存分配是人们最常提到的性能惩罚std::function.

我最近写了一篇文章,其中包含更多细节,并解释了如何(以及在​​何处)避免支付内存分配的代价.

http://drdobbs.com/cpp/232500059

  • @Ruslan 可悲的是。不幸的是 DrDobbs 几年前关闭了,我不知道旧内容发生了什么。我在任何地方都找不到我的文章。我对此感到抱歉和悲伤:-( (2认同)
  • @MohammedNoureldin 太可惜了。正如我在另一条评论中所说,DrDobbs 几年前就关门了。有时我可以在某处找到旧内容,有时却找不到。我不知道我是否保留了这篇文章的副本。即使我这样做了,我也不知道我是否可以在其他地方发布/发布它。作者常常被要求将版权交给出版商并失去他们的权利。(虽然多布斯博士已经死了,但他们的律师可能还醒着。)我不记得这篇文章的情况是否如此。如果可以的话,我会尽力恢复它,但我不能保证任何事情。对此我真的很抱歉。 (2认同)

Unc*_*ens 14

您可以从boost的参考资料中找到信息:通过boost :: function调用会产生多少开销?表现

这并不能确定"是或否"来提升功能.考虑到程序的要求,性能下降可能是可以接受的.通常,程序的某些部分对性能不重要.即使这样,也可以接受.这只是您可以确定的内容.

对于标准库版本,标准仅定义了一个接口.完全由个人实现来使其工作.我想将使用类似于boost函数的实现.


lur*_*her 11

如果您在不绑定任何参数(不分配堆空间)的情况下传递函数,这将强烈依赖.

还取决于其他因素,但这是主要因素.

确实,你需要比较一些东西,你不能简单地说它"减少了开销",而不是完全不使用它,你需要将它与使用另一种传递函数的方法进行比较.如果您可以完全免除使用它,那么从一开始就不需要它

  • 如果实现使用小缓冲区优化将函数对象存储在`std :: function`实例中,并且传递的callable在SBO的合适大小范围内,那么即使绑定参数也可能不会产生动态分配. (3认同)

Seb*_*ach 11

首先,函数内部的开销变小; 工作量越大,开销越小.

其次:与虚函数相比,g ++ 4.5没有显示出任何差异:

main.cc

#include <functional>
#include <iostream>

// Interface for virtual function test.
struct Virtual {
    virtual ~Virtual() {}
    virtual int operator() () const = 0;
};

// Factory functions to steal g++ the insight and prevent some optimizations.
Virtual *create_virt();
std::function<int ()> create_fun();
std::function<int ()> create_fun_with_state();

// The test. Generates actual output to prevent some optimizations.
template <typename T>
int test (T const& fun) {
    int ret = 0;
    for (int i=0; i<1024*1024*1024; ++i) {
        ret += fun();
    }    
    return ret;
}

// Executing the tests and outputting their values to prevent some optimizations.
int main () {
    {
        const clock_t start = clock();
        std::cout << test(*create_virt()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "virtual: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun_with_state()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function with bindings: " << secs << " secs.\n";
    }
}
Run Code Online (Sandbox Code Playgroud)

impl.cc

#include <functional>

struct Virtual {
    virtual ~Virtual() {}
    virtual int  operator() () const = 0;
};
struct Impl : Virtual {
    virtual ~Impl() {}
    virtual int  operator() () const { return 1; }
};

Virtual *create_virt() { return new Impl; }

std::function<int ()> create_fun() { 
    return  []() { return 1; };
}

std::function<int ()> create_fun_with_state() { 
    int x,y,z;
    return  [=]() { return 1; };
}
Run Code Online (Sandbox Code Playgroud)

产量g++ --std=c++0x -O3 impl.cc main.cc && ./a.out:

1073741824
virtual: 2.9 secs.
1073741824
std::function: 2.9 secs.
1073741824
std::function with bindings: 2.9 secs.
Run Code Online (Sandbox Code Playgroud)

所以,不要害怕.如果您的设计/可维护性可以通过优先std::function于虚拟呼叫来改进,请尝试使用它们.就个人而言,我真的很喜欢不强制我的类的客户端上的接口和继承.

  • g ++ 6.3.0:g ++ -std = gnu ++ 14 -O3 -flto -march = native impl.cpp main.cpp && ./a.out 1073741824 virtual:1.97619 secs.1073741824 std :: function:6.86855秒.1073741824 std ::带绑定的函数:6.86847秒. (3认同)
  • @Xeo:是的。但是,验证比相信要好:)如果不使用优化,则同一测试与`std :: function`会显示1:3的差异,因此该测试并非完全没有道理。 (2认同)
  • 使用 G++ 4.8.2,我始终获得 2.9、3.3 和 3.3 秒。如果我添加`-flto`,它们都会变成3.3。我完全疯狂的猜测是,GCC 实际上尝试优化 `std::function` (类似于使用 `-flto` 和虚拟函数得到的结果),但优化实际上会造成伤害。 (2认同)
  • 使用g ++ 5.3,我得到2.0、2.3、2.3(-O2); 0.7、2.0、2.0(-O2 -flto); 2.3、2.3、2.3(-O2 -flto -fno-devirtualize); 2.0、2.3、2.3(-O2 -fno-devirtualize)。因此,似乎在较新的g ++版本中取消虚拟化已得到足够的改进,以至于不再是取消优化。 (2认同)
  • Ubuntu 18.04 (AMD 2400G) 上的 g++ 7.4.0:` g++ --std=c++17 -O3 impl.cc main.cc &amp;&amp; ./a.out`:虚拟:1.38742 秒,std::function:1.44681 secs., std::function with bindings: 1.39367 secs. (2认同)