31

C++ 仮想関数は、C スタイルの関数ポインターを呼び出すのと同じくらい速く、ポリモーフィックな基底クラスで呼び出されますか? 本当に違いはありますか?

関数ポインターを利用し、それらをポリモーフィズムの仮想関数に変更する、パフォーマンスを重視したコードをリファクタリングすることを検討しています。

4

5 に答える 5

35

ほとんどの C++ 実装はこれと同様に機能すると思います (そしておそらく、C にコンパイルされた最初の実装では、次のようなコードが生成されました)。

struct ClassVTABLE {
    void (* virtuamethod1)(Class *this);
    void (* virtuamethod2)(Class *this, int arg);
};

struct Class {
    ClassVTABLE *vtable;
};

次に、インスタンスが与えられた場合Class x、そのメソッドを呼び出すのvirtualmethod1は のようなものですx.vtable->virtualmethod1(&x)。したがって、1 つの余分な逆参照、1 つのインデックス付きルックアップvtable、および 1 つの追加の引数 (= this) がスタックにプッシュされ、レジスタに渡されます。

ただし、コンパイラはおそらく、関数内のインスタンスで繰り返されるメソッド呼び出しを最適化できます。インスタンスClass xは構築後にクラスを変更できないため、コンパイラは全体x.vtable->virtualmethod1を共通のサブ式と見なし、ループから移動できます。したがって、この場合、単一の関数内で繰り返される仮想メソッド呼び出しは、単純な関数ポインターを介して関数を呼び出すのと同じ速度になります。

于 2013-07-30T23:42:50.330 に答える
8

多くの違いが見られる可能性は低いですが、これらすべてのことと同様にthis、パフォーマンスの違いを引き起こす可能性があるのは、多くの場合、小さな詳細 (コンパイラが仮想関数にポインターを渡す必要があるなど) です。関数自体は「virtualボンネットの下」の関数ポインターであるため、コンパイラーが処理を完了すると、おそらく両方のケースでかなり似たコードが得られます。

仮想関数をうまく使っているように聞こえますが、もし誰かが反対して「パフォーマンスの違いがあるだろう」と言ったら、私は「それを証明してください」と言うでしょう。しかし、その議論を避けたい場合は、既存のコードのパフォーマンスを測定するベンチマーク (まだない場合) を作成し、それ (またはその一部) をリファクタリングして、結果を比較してください。理想的には、いくつかの異なるマシンでテストして、自分のマシンではうまく機能するが、他のタイプのマシン (異なる世代のプロセッサ、異なる製造元またはプロセッサなど) ではそれほどうまくいかない結果が得られないようにします。

于 2013-07-30T23:42:00.933 に答える
7

仮想関数呼び出しには 2 つの逆参照が含まれ、そのうちの 1 つはインデックス付き、つまり のようなもの*(object->_vtable[3])()です。

関数ポインターを介した呼び出しには、1 つの逆参照が含まれます。

メソッド呼び出しでは、隠し引数を として受け取る必要もありますthis

メソッド本体が実質的に空で、引数や戻り値がない場合を除き、違いに気付く可能性はほとんどありません。

于 2013-07-30T23:42:36.070 に答える
3

関数ポインター呼び出しと仮想関数呼び出しの違いは、上記がボトルネックであることを既に測定していない限り、無視できます。

唯一の違いは次のとおりです。

  • 仮想関数には、vtable のメモリ読み取りと、関数のアドレスへの間接呼び出しがあります。
  • 関数ポインターには、関数への間接呼び出しが 1 つだけあります。

これは、仮想関数が呼び出す関数のアドレスをルックアップする必要があるためですが、関数ポインターは既にそれを認識しています (それ自体に格納されているため)。

C++ を使用しているため、仮想メソッドを使用する必要があります。

于 2013-07-30T23:43:20.077 に答える