Fast Delegate(等)背后的想法是否用于优化std :: function?

Emi*_*ier 22 c++ performance boost c++11 std-function

已经提出了C++"委托"的建议,其开销低于boost::function:

是否已将这些想法用于实施std::function,从而产生更好的性能boost::function?有谁比较过std::functionvs 的表现boost::function

我想知道这个特别适用于英特尔64位架构上的GCC编译器和libstdc ++,但欢迎其他编译器的信息(例如Clang).

Jon*_*ely 28

在libstdc ++中,std::function我们使用一个适当大小和对齐的联合类型来存储指针,函数指针或指向成员函数的指针.我们避免为可以存储在该大小和对齐中的任何函数对象进行堆分配,仅限于它是"位置不变"的

/**
 *  Trait identifying "location-invariant" types, meaning that the
 *  address of the object (or any of its members) will not escape.
 *  Also implies a trivial copy constructor and assignment operator.
 */
Run Code Online (Sandbox Code Playgroud)

代码基于std::tr1::function实现,该部分没有显着变化.我认为这可以简化使用,std::aligned_storage并且可以通过专门化特性来改进,以便将更多类型识别为位置不变.

调用目标对象是在没有任何虚函数调用的情况下完成的,类型擦除是通过在std::function函数模板特化的地址中存储单个函数指针来完成的.所有操作都是通过存储的指针调用该函数模板并传入一个枚举来识别它被要求执行的操作来完成的.这意味着没有vtable,只需要在对象中存储单个函数指针.

这个设计是由boost::function原作者提供的,我相信它接近于提升实施.有关基本原理,请参阅Boost.Function 的Performance文档.这意味着GCC的std::function速度不太快boost::function,因为它是同一个人的类似设计.

注意我们std::function不支持使用分配器构建,它需要做的任何分配都将使用new.


为了回应Emile的评论,表达了避免堆分配的意图,该分配std::function包含指向成员函数和对象的指针,这里有点破解(但你没有听到我的意思;-)

struct A {
  int i = 0;
  int foo() const { return 0; }
};

struct InvokeA
{
  int operator()() const { return a->foo(); }
  A* a;
};

namespace std
{
  template<> struct __is_location_invariant<InvokeA>
  { static const bool value = true; };
}

int main()
{
  A a;
  InvokeA inv{ &a };

  std::function<int()> f2(inv);

  return f2();
}
Run Code Online (Sandbox Code Playgroud)

诀窍是它InvokeA足够小以适应function小的对象缓冲区,并且特征专门化说它可以安全地存储在那里,因此function它直接保存该对象的副本,而不是堆上.a只要指向它的指针仍然存在,这需要持久化,但如果function目标是这样的话,情况就是如此bind(&A::foo, &a).

  • 我刚刚提交了一个更改,使上面的`__is_location_invariant`专门化不必要,所以GCC 5.0不会为`InvokeA'分配内存 (5认同)
  • 哇,马口的海峡!如果我从这个表达式`std :: bind(&Object :: memberFunction,objectInstance)`构造一个`std :: function`,它会构成"位置不变"吗? (4认同)
  • 不,那不是位置不变的,因为该绑定表达式的结果将包含指向成员函数的指针_和_对象的副本!我计划增加union的大小,以便它可以保存一个指向成员和指针的指针,这样`bind(&O :: f,&o)`就可以了,但这样会比较棘手. (4认同)
  • 我认为通过`std :: function`调用绑定到对象实例的成员函数是一个非常常见的用例,所以我很高兴看到一个`std :: function`实现,它避免了针对这种情况的堆分配. (3认同)