web-dev-qa-db-ja.com

メソッド/プロパティを仮想としてマークすることのパフォーマンスへの影響は何ですか?

質問はタイトルに記載されているとおりです。メソッド/プロパティを仮想としてマークすることのパフォーマンスへの影響は何ですか?

注-一般的なケースでは、仮想メソッドがnotオーバーロードされると想定しています。私は通常、ここで基本クラスを使用します。

63
Erik Forbes

仮想関数は、直接呼び出しと比較して、パフォーマンスのオーバーヘッドが非常に小さいだけです。低レベルでは、基本的に配列ルックアップを調べて関数ポインターを取得し、次に関数ポインターを介して呼び出します。最新のCPUは、分岐予測子で間接関数呼び出しをかなり適切に予測できるため、通常、最新のCPUパイプラインにそれほど大きな影響を与えることはありません。アセンブリレベルでは、仮想関数呼び出しは次のように変換されます。ここで、Iは任意の即時値です。

MOV EAX, [EBP + I] ; Move pointer to class instance into register
MOV EBX, [EAX] ;  Move vtbl pointer into register.
CALL [EBX + I]  ;   Call function

対直接関数呼び出しの場合は次のとおりです。

CALL I  ;  Call function directly

実際のオーバーヘッドは、ほとんどの場合、仮想関数をインライン化できないことです。 (VMが、とにかく常に同じアドレスに移動することを認識している場合は、JIT言語にすることができます。)インライン化によって得られる高速化に加えて、インライン化により、定数畳み込みなどの他のいくつかの最適化が可能になります。 、呼び出し元は呼び出し先が内部でどのように機能するかを知ることができるためです。インライン化できないほど大きい関数の場合、パフォーマンスへの影響は無視できる可能性があります。インライン化される可能性のある非常に小さい関数の場合は、仮想に注意する必要があります。機能。

編集:覚えておくべきもう一つのことは、すべてのプログラムがフロー制御を必要とし、これは決して無料ではないということです。仮想関数を置き換えるものは何ですか? switchステートメント?一連のifステートメント?これらはまだ予測できないかもしれないブランチです。さらに、Nウェイ分岐が与えられると、一連のifステートメントはO(N)で適切なパスを見つけ、仮想関数はO(1)でそれを見つけます。 switchステートメントは、ジャンプテーブルに最適化されているかどうかに応じて、O(N)またはO(1))になります。

139
dsimcha

Rico Marianiは、彼の Performance Tidbitsブログ でパフォーマンスに関する問題の概要を説明しています。

仮想メソッド:直接呼び出しを行う場合に仮想メソッドを使用していますか?多くの場合、人々は将来の拡張性を可能にするために仮想メソッドを使用します。拡張性は良いことですが、それは代償を伴います。完全な拡張性のストーリーが完成し、仮想関数を使用することで実際に必要な場所に到達できることを確認してください。たとえば、コールサイトの問題について考えても、「拡張」オブジェクトがどのように作成されるかを考えない場合があります。後で彼らは、(ほとんどの)仮想関数がまったく役に立たず、「拡張された」オブジェクトをシステムに取り込むためにまったく異なるモデルが必要であることに気付きました。

シーリング:シーリングは、クラスのポリモーフィズムを、ポリモーフィズムが必要なサイトだけに制限する方法です。タイプを完全に制御する場合、直接呼び出しとインライン化が可能になるため、シーリングはパフォーマンスにとって優れたものになる可能性があります。

基本的に、仮想メソッドに対する反論は、直接呼び出しではなく、コードがインライン化の候補になることを許可しないというものです。

MSDNの記事 。NETアプリケーションのパフォーマンスとスケーラビリティの向上 では、これについてさらに詳しく説明しています。

仮想メンバーのトレードオフを検討してください

仮想メンバーを使用して拡張性を提供します。クラスの設計を拡張する必要がない場合は、仮想テーブルのルックアップのために呼び出すコストが高く、特定の実行時のパフォーマンスの最適化が無効になるため、仮想メンバーは避けてください。たとえば、仮想メンバーをコンパイラーでインライン化することはできません。さらに、サブタイピングを許可すると、実際には非常に複雑なコントラクトがコンシューマーに提示され、将来クラスをアップグレードしようとすると、必然的にバージョン管理の問題が発生します。

ただし、上記の批判は、TDD/BDDキャンプ(メソッドをデフォルトで仮想化することを望んでいる)から来ており、特にはるかに高速なマシンにアクセスできるため、パフォーマンスへの影響は無視できると主張しています。

16
Jon Limjap

通常、仮想メソッドは、実際のメソッドに到達するために1つの関数ポインターのテーブルを通過するだけです。これは、1つの追加の逆参照と、メモリへのもう1つのラウンドトリップを意味します。

コストは完全にゼロではありませんが、それは非常に最小限です。プログラムが仮想関数を持つのに役立つ場合は、ぜひそれを実行してください。

Vテーブルを回避するためだけに、不器用なプログラムよりも、小さな、小さな、小さなパフォーマンスヒットを備えた適切に設計されたプログラムを用意する方がはるかに優れています。

11
abelenky

.NET JITコンパイラーは、いくつかの(多くの?)ケースでオーバーヘッドを最適化できる可能性があるため、確実に言うのは難しいです。

しかし、それが最適化されない場合は、基本的に、余分なポインターの間接参照について話します。

つまり、非仮想メソッドを呼び出すときは、次のことを行う必要があります。

  1. レジスタを保存し、引数を設定するための関数プロローグ/エピローグを生成し、戻り値などをコピーします。
  2. 固定された静的に既知のアドレスにジャンプします

1はどちらの場合も同じです。 2の場合、仮想メソッドでは、代わりにオブジェクトのvtableの固定オフセットから読み取り、そのポイントにジャンプする必要があります。これにより、分岐予測が困難になり、CPUキャッシュから一部のデータがプッシュされる可能性があります。したがって、違いはそれほど大きくはありませんが、すべての関数呼び出しを仮想化すると、合計が増える可能性があります。

また、最適化を阻害する可能性があります。コンパイラは、呼び出される関数を正確に認識しているため、非仮想関数の呼び出しを簡単にインライン化できます。仮想関数では、それは少しトリッキーです。 JITコンパイラーは、呼び出される関数が決定されると、それを実行できる可能性がありますが、それははるかに多くの作業です。

全体として、特にパフォーマンスが重要な領域では、それでも合計することができます。ただし、関数が少なくとも1秒間に数十万回呼び出されない限り、心配する必要はありません。

4
jalf

あなたのタグから、あなたはc#を話している。私はデルファイの観点からしか答えることができません。似ていると思います。 (私はここで負帰還を期待​​しています:))

静的メソッドはコンパイル時にリンクされます。仮想メソッドでは、呼び出すメソッドを決定するために実行時にルックアップが必要になるため、オーバーヘッドはわずかです。メソッドが小さく、頻繁に呼び出される場合にのみ重要です。

3
Richard A

このテストはC++で実行しました 。仮想関数呼び出しは、(3ghz PowerPCでは)直接関数呼び出しよりも7〜20ナノ秒長くかかります。つまり、実際には、1秒間に100万回呼び出す予定の関数、またはオーバーヘッドが関数自体よりも大きくなる可能性があるほど小さい関数にのみ問題があります。 (たとえば、アクセサ関数を盲目的な習慣から仮想化することはおそらく賢明ではありません。)

私はC#でテストを実行していませんが、CLRのほぼすべての操作にはとにかく間接的なものが含まれるため、違いはさらに少なくなると思います。

3
Crashworks

デスクトップ側では、メソッドがオーバーロードされているかどうかは関係ありません。メソッドポインタテーブル(仮想メソッドテーブル)を介して余分なレベルの間接が発生します。つまり、メソッド呼び出しが比較される前に、間接を介して約2つの余分なメモリが読み取られます。非シールクラスおよび非最終メソッドの非仮想メソッド。

[興味深い事実として、コンパクトフレームワークバージョン1.0では、仮想メソッドテーブルを使用せず、仮想メソッドを呼び出すときに実行する適切なメソッドを見つけるためのリフレクションを使用するため、過熱が大きくなります。]

また、仮想メソッドは、非仮想メソッドよりもインライン化や末尾呼び出しなどの他の最適化の候補になる可能性がはるかに低くなります。

大まかに言うと、これはメソッド呼び出しのパフォーマンス階層です。

非仮想メソッド<仮想メソッド<インターフェイスメソッド(クラス上)<デリゲートディスパッチ<MethodInfo.Invoke <Type.InvokeMember

しかし、さまざまなディスパッチメカニズムのこれらのパフォーマンスへの影響は、測定;)で証明しない限り、重要ではありません(それでも、アーキテクチャへの影響、読みやすさなどは、どちらを選択するかによって大きな重みを持つ可能性があります)

0
Pop Catalin