2009-05-20 26 views
11

私の同僚の一人は、インターフェイスを実装するとオーバーヘッドが発生すると話しました。これは本当ですか?インターフェイスの実装のオーバーヘッド

私はマイクロ最適化について心配していません。私はちょうどこれが伴うより深い詳細を知りたい。

答えて

18

は抵抗できず、テストしてもほとんどオーバーヘッドのようには見えません。

参加者は、次のとおり

Interface IFoo defining a method 
class Foo: IFoo implements IFoo 
class Bar   implements the same method as Foo, but no interface involved 

私は

Foo realfoo = new Foo(); 
IFoo ifoo = new Foo(); 
Bar bar = new Bar(); 

を定義し、各変数に20の文字列の連結、10,000,000回を行う方法と呼ばれます。

realfoo: 723 Milliseconds 
ifoo:  732 Milliseconds 
bar:  728 Milliseconds 

このメソッドが何もしない場合、実際の呼び出しはもう少し目立ちます。

realfoo: 48 Milliseconds 
    ifoo: 62 Milliseconds 
    bar: 49 Milliseconds 
+0

テストに感謝します。 –

+3

インタフェースで宣言されたメソッドは自動的に "仮想"であることに注意してください。これは、JITコンパイラがこのようなメソッド呼び出しをインライン化することは決してないことを意味します。したがって、1行のメソッドでインターフェイスを実装する場合は、「[MethodImpl(MethodImplOptions.AggressiveInlining)]」(C#)を使用しても呼び出しコストを支払うことになります。 – Paya

3

オーバーヘッドとは何か?

インターフェイス経由での呼び出しは、非仮想メソッドへの呼び出しよりも高価です。私は個人的にそれをテストしていないが、私はそれが仮想呼び出しに大きさが似ていると思う。

ほとんどの場合、パフォーマンスは通常、何かのためにインターフェイスを使用しない正当な理由ではありません。ほとんどの場合、通話量は問題には十分ではありません。

+0

ありがとうございました。それらを実装しない場合と比較してオーバーヘッド。暗黙的な実装はどうですか?あなたは普通の方法と同じスピードを持っていますか? –

+0

あなたは自分自身を見つけるためにそれを単に測定してみませんか? –

+0

私はできますが、私はどちらが速いのか興味がありませんが、なぜですか。 –

9

メソッドを呼び出すとき、またはプロパティにアクセスするときに余分な間接化が行われるため、インターフェイスにオーバーヘッドが発生します。インタフェースの実装を含む、多態性を実装する多くのシステムは、一般的に、実行時の型に基づいて関数呼び出しをマッピングする仮想メソッドテーブルを使用します。

理論的には、コンパイラは、呼び出しが行われているオブジェクトの履歴をコンパイラが証明できるという条件で、仮想関数呼び出しを通常の関数呼び出しまたはインラインコードに最適化することができます。

ほとんどの場合、仮想関数呼び出しを使用するメリットは、欠点になります。

+3

Javaでは少なくとも、すべての関数呼び出しは仮想的なものなので、測定可能なオーバーヘッドがあるのではないでしょうか。 – Jherico

+0

これは本当らしいです。 – user109878

4

はい、インターフェイスにオーバーヘッドが発生します。実際、ロジックとプロセッサの間に追加するレイヤーによって、オーバーヘッドが増加します。明らかに、オーバーヘッドを発生させない唯一のものであるため、アセンブリ内のすべてを記述する必要があります。 GUIにもオーバーヘッドが発生するので、それらを使用しないでください。

私は面倒ですが、ポイントは、明確で分かりやすく保守可能なコードとパフォーマンスの間で独自のバランスを見つけなければならないことです。アプリケーションの99.999%(もちろん繰り返されます)では、より高価なメソッドの実行を不必要に繰り返さないように心がけている限り、メンテナンスを難しくする必要はありませんちょうどそれがより速く動くようにするためです。

+3

99.999 ...%== 100%((http://en.wikipedia.org/wiki/.999))したがって、パフォーマンスの維持管理能力を犠牲にする必要のないアプリケーションはないと言います。 –

+0

なぜdownvotes ?!彼は冗談をしています。 –

+2

早期最適化はすべての悪の根源です。最適化されていないアプリが仕様を満たしていないと判断して、「最適化」の前後でテストして仕様を満たしていることを確認するまでは最適化しないでください(そうでない場合はロールバックしてください)それが失敗した方法を正確に言っているコメントとしてそこにコードを書いてください。他の人はあなたが何をしているのか分からず、読みやすさのためにそれを最適化しないと仮定しません。 –

6

私はマイクロ最適化については心配していませんが、これが必要とするより深い詳細を知りたいだけです。

オーバーヘッドがありますが、それはマイクロ最適化レベルオーバーヘッドです。たとえば、インターフェイスはILからcallvirtに切り替える際に多くの呼び出しを行うことができますが、それは信じられないほどマイナーです。

+0

ありがとうリード。 callvirtスイッチへの呼び出しはどういう意味ですか? –

+3

ILでは、メソッドを呼び出すための "call"と、仮想関数を呼び出す "callvirt"の2つの異なる呼び出しがあります。 CallvirtはCLR内で仮想メソッドテーブルルックアップを強制するため、「呼び出し」関数よりも速度が遅くなります(これが、クラスをシーリングすることでマイクロオプトレベルでパフォーマンスを向上させることができます)。インタフェースを使用すると、インタフェース内のメソッド呼び出しがcallvirtを使用します。技術的にcallvirtを使用する必要がない場合もあります。 –

+0

ありがとうリード。あなたは時には彼らがしなければならないと言いました、いつそれが起こりますか?インタフェースメンバを暗黙的に呼び出すとき? –

2

インターフェイスにオーバーヘッドが発生してはいけませんが、何とかしています。私はこれを直接知っているわけではありませんが、中古品ではケーブルボックスで作業しているため、性能の低いシナリオをテストするには力が足りません。 (インスタンス化されたクラスの数には大きな違いがあります)。

実行時にインタフェースに与える影響が非常に小さいため、呼び出しがインターフェイスを「通過」するようなものではなく、インタフェースは単にコンパイラが2つのコードをリンクする方法です。実行時には、ポインタディスパッチテーブル以外のものであってもかまいません。

まだ、パフォーマンスに影響します。私はそれがメタデータ/リフレクションと関係していると推測しています。なぜなら、メタデータを必要としなければ、インターフェイスが使用されるのは、あまり具体的でないインターフェイスからそのインターフェイスにキャストするときだけです。可能かどうかを確認して確認するタグになります。

私は技術的な理由を知っているかどうか知りたいので、この質問に従います。まったく同じ原因になるため、Javaに拡張したいことがあります。すべてがオープンソースであるため、Javaで答えが得られる可能性が高くなります。

+0

私はオーバーヘッドがランタイムタイプの決定にあると思う。しかし、私の理解では、各オブジェクトには独自の仮想メソッドテーブルがあり、構築時に初期化が行われるということです。両方の呼び出しは通常同じ仮想メソッドテーブルを経由しなければならないので、インタフェースからメソッドを間接的に呼び出すことは、オブジェクト自体からの直接呼び出しとは多少異なる方法で実行するとは思いません。 – user109878

4

私たちはゲームを作っており、60fpsで動作しなければならず、データレイアウト、キャッシュコヒーレンシ、データの変換が非常に重要な場合を除き、ソフトウェアの作成中にインターフェイスコールの余分なパフォーマンスコストについてそれは、選択された状況で顕著になるだけです。

たとえば、タイトなループでインターフェイスを介して呼び出しを行う(そしてメソッドの実行コストがインタフェース呼び出しのオーバーヘッドを大きくする可能性があります)、またはデータの処理中に「ポインタを追う」ことがあります。

また、コールをより粗くするためにインタフェースの契約を変更することを止める方法はありません。 の代わりにIMyInterface.Process(Car car)

スティーブマッコネルは、一定のマイクロ最適化に対してアドバイスしています。優れたプラクティス(保守性、可読性などに集中する)を使ってプログラムを作成し、パフォーマンスが十分でない場合は、プロファイルを作成して主要なボトルネックを修正するための手順を実行することをお勧めします。

の方が高速であるため、投機的にすべてのコードを最適化することに意味はありません。あなたの実行時間の80%がコードの20%を実行するのに費やされた場合、ここでかそこらで10マイクロ秒を削り取るかもしれないという理由だけでどこでも疎結合を犠牲にするのは明らかに愚かです。したがって、10マイクロ秒を節約することができますが、他の機能がCPUをうまく動かしている場合、プログラムはそれ以上速くなりません。

非重要なソフトウェアで作業している場合は、インターフェイスをマイクロ最適化として回避します。また、必要に応じて後で簡単に取り除くこともできます(ソフトウェアをすぐに出荷しないと仮定した場合)。ほとんどのソフトウェアは高速な速度を必要としません。例外(ゲーム、シミュレーター、リアルタイム株取引など)はありますが、その場合でも、必ずしもインターフェースではないことが原因です。 のJavaの観点からいえば

+0

それは私が不思議に思っているものです。つまり、インターフェイスの外部(暗黙的に)インターフェイスメソッドを呼び出すと、通常のメソッド呼び出しと同じになりますか? –

+1

エリックによると、はい。 http://blogs.msdn.com/ericgu/archive/2004/03/19/92911.aspx –

12

は、少なくともホットスポットの最近のバージョンでは、答えは一般的にほとんど、あるいはそれがを重要オーバーヘッドなしです。例えば

、あなたは次のような方法を持っていると仮定:

public void doSomethingWith(CharSequence cs) { 
    char ch = cs.charAt(0); 
    ... 
} 

たCharSequenceは、当然のことながら、インターフェースです。だから、あなたはメソッドが余分な仕事をして、どのオブジェクトタイプをチェックし、メソッドを見つけて、おそらくインターフェイスを最後に検索するなど、おそらくあなたが想像することができる恐ろしい物語すべてを恐れていると思うかもしれません...

しかし、実際には、VMはそれよりもはるかに巧妙である可能性があります。実際には、特定の型のオブジェクトを常に渡しているので、objec型チェックをスキップできるだけでなく、メソッドをインライン化することもできます。例えば、一連のStringのループで上記のようなメソッドを呼び出すと、HotspotはcharAt()の呼び出しを実際にインライン化できるため、文字を文字通り2つのMOV命令になるようにします。つまり、インターフェイス上のメソッド呼び出しは、メソッド呼び出しが全くなくてもに変わる可能性があります。 (この情報は、1.6アップデート12のデバッグバージョンからのアセンブリ出力に基づいています。)

+0

非常に良い情報です。 –

3

残念ながら、Javaでは残念ながらインターフェイスのパフォーマンスを向上させるためにいくつかの最適化が行われています。はい、invokesvirtualとinvokeinterface命令にはほとんどオーバーヘッドがありませんが、invokespecialと比較しますが、非常に一般的なインターフェイスの非常に一般的な使用方法でパフォーマンスの欠点を狙ったDa Vinci projectがあります。

詳細については、this Java bugparade request for enchancementを参照してください。

いつものように(これは分かっているようですが)Amdahl's Lawに相談してみましょう。多くのメソッド呼び出しを行い、速度が必要な場合は、徹底的なベンチマークと組み合わせてリファクタリングを検討してください。

1

インターフェイスを使用した呼び出しは、vtableの余分なインダイレクションレイヤのために、他の形式の仮想メソッド呼び出しよりも若干コストがかかります。大多数のケースでは、これは重要ではありませんので、パフォーマンスについて心配する必要はなく、良いデザインに固執してください。

最近、インターフェイスを導入してすべての呼び出しをインターフェイスで行うことで、いくつかのクラスをリファクタリングしました。私はパフォーマンスチェックをしなくてもそれをリリースしたので、これが影響を受けないと自信が持てました(または怠け者でした)。これはアプリケーション全体のパフォーマンス(コールだけでなく)に10%の影響を与えたことが判明しました。私たちはいくつかの変更を加えましたが、これが私たちが疑った最後のものでした。最終的に私たちが具体的なクラスに戻ったとき、元のパフォーマンスが復元されました。

これは大幅に最適化されたアプリケーションであり、上記以外の場合は適用されないことがあります。

+0

ほとんどの場合、仮想メソッド呼び出しとインターフェイス経由の呼び出しはまったく同じものです。 – user109878

+0

オブジェクトは多くのインタフェースを実装することができますが、単一の基本クラスのみを持つためです。つまり、親クラスから来たメソッドのvtableは予測可能であり、直接使用することができます。インタフェースvtableには、オブジェクトが任意の数のインタフェースを実装できるため、このような予測可能なレイアウトはありません。 – BeeOnRope

2

私はsmall benchmarkを実装しました。私の考えによれば、このコードはJVMがメソッドの解決結果をキャッシュするのをブロックするはずだから、Javaでinvokeinterfaceinvokevirtualの間に明確な違いがあるはずです。

ベンチマークの結果は、invokeinterfaceINVOKEVIRTUALより38%遅いことです。

関連する問題