22

よりもオーバーヘッドが低い C++ の「デリゲート」の提案がありましたboost::function

これらのアイデアのいずれかを実装するために使用したstd::function結果、 よりも優れたパフォーマンスが得られましたboost::functionか? std::functionvsのパフォーマンスを比較した人はいboost::functionますか?

Intel 64 ビット アーキテクチャの GCC コンパイラと libstdc++ について具体的に知りたいのですが、他のコンパイラ (Clang など) に関する情報も大歓迎です。

4

2 に答える 2

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.
 */

std::tr1::functionコードは実装に基づいており、その部分は大幅に変更されていません。std::aligned_storageより多くのタイプが位置不変として識別されるように、トレイトを特殊化することで、それを使用して簡素化し、改善できると思います。

ターゲット オブジェクトの呼び出しは、仮想関数呼び出しなしで行われます。型の消去はstd::function、関数テンプレートの特殊化のアドレスである に単一の関数ポインターを格納することによって行われます。すべての操作は、格納されたポインターを介してその関数テンプレートを呼び出し、実行するように求められている操作を識別する列挙型を渡すことによって行われます。これは、vtable がなく、単一の関数ポインターのみをオブジェクトに格納する必要があることを意味します。

この設計は元の作成boost::function者によって提供されたもので、boost の実装に近いと思います。理由については、Boost.Functionのパフォーマンスドキュメントを参照してください。つまり、GCCは同じ人物による同様の設計であるため、 GCCstd::functionが よりも高速である可能性はほとんどありません。boost::function

std::function: アロケータを使用した構築はまだサポートされていません。必要な割り当ては を使用して行われnewます。


メンバー関数とオブジェクトへのポインターを保持するa のヒープ割り当てを回避したいという 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();
}

トリックは、InvokeAが の小さなオブジェクト バッファーに収まるほど小さいことですfunction。特性の特殊化では、そこに格納しても安全であると示されているため、functionはそのオブジェクトのコピーをヒープ上ではなく直接保持します。これはa、それへのポインターが持続する限り持続する必要がありますが、functionのターゲットがである場合はとにかくそうですbind(&A::foo, &a)

于 2012-06-20T20:34:32.587 に答える
4

コメントに記載されているように、std::function は単なるインターフェイスであり、実装が異なれば実行されることも異なりますが、この問題について実際に標準が何か言いたいことがあることに注意してください。20.8.11.2.1/5 から (標準の一部というよりも IP アドレスのように見えます):

注:実装では、呼び出し可能な小さなオブジェクトに動的に割り当てられたメモリを使用しないようにすることをお勧めします。たとえば、f のターゲットは、オブジェクトへのポインターまたは参照とメンバー関数ポインターのみを保持するオブジェクトです。—終わりのメモ

これは、デリゲートに関する引用された記事によって動機付けられた「小さな関数の最適化」を採用することを実装者に奨励する標準の方法です。(記事自体は、実際には .NET の意味でのデリゲートについては説明していません。むしろ、バインドされたメンバー関数を意味するために「デリゲート」という用語を使用しています。)

于 2012-10-21T16:59:18.840 に答える