javaメソッド呼び出しのコスト


82

私は初心者で、コードを繰り返すのは悪いことだといつも読んでいます。ただし、そうしないためには、通常、追加のメソッド呼び出しが必要になるようです。次のクラスがあるとしましょう

public class BinarySearchTree<E extends Comparable<E>>{
    private BinaryTree<E> root;
    private final BinaryTree<E> EMPTY = new BinaryTree<E>();
    private int count;
    private Comparator<E> ordering;

    public BinarySearchTree(Comparator<E> order){
        ordering = order;
        clear();
    }

    public void clear(){
        root = EMPTY;
        count = 0;
    }
}

実際のメソッドを呼び出す代わりに、clear()メソッドの2行をコピーしてコンストラクターに貼り付ける方が最適でしょうか?もしそうなら、それはどのくらいの違いを生むのでしょうか?コンストラクターが10回のメソッド呼び出しを行い、それぞれがインスタンス変数を値に設定するだけの場合はどうなりますか?プログラミングのベストプラクティスは何ですか?


4
ただし、ここでメソッドを呼び出すと、2番目のメソッド呼び出しが完全に無料でスローされます。送料と手数料のみお支払いください!真剣に、しかし。ロードするコードを増やすことによるオーバーヘッドと同様に、メソッド呼び出しにもオーバーヘッドがあります。ある時点で、一方が他方よりも高価になります。伝える唯一の方法は、コードをベンチマークすることです。
マークB

11
3 ... 2 ... 1

22
これに対する反対票の理由はわかりません-男は完全に正当な質問をしています。一部の人にとっては明白な答えかもしれませんが、それは悪い質問にはなりません!
マイケルベリー

3
確かに、それは非常に正当な質問です。反対票を投じる理由は、完全に重複している場合だけかもしれません。
アラファンギオン2011年

1
明らかな場合は申し訳ありませんが、私は自分で学んでおり、数か月しか学習していません。オンラインのサンプルコードで見たいくつかのことは、良い習慣ではないことがわかったので、もう一度確認したいと思います。
jhlu87 2011年

回答:


74

実際のメソッドを呼び出す代わりに、clear()メソッドの2行をコピーしてコンストラクターに貼り付ける方が最適でしょうか?

コンパイラーはその最適化を実行できます。また、JVMも同様です。コンパイラの作成者とJVMの作成者が使用する用語は、「インライン展開」です。

もしそうなら、それはどのくらいの違いを生むのでしょうか?

それを測定します。多くの場合、違いがないことがわかります。そして、これがパフォーマンスのホットスポットであると信じている場合は、間違った場所を探しています。それがあなたがそれを測定する必要がある理由です。

コンストラクターが10回のメソッド呼び出しを行い、それぞれがインスタンス変数を値に設定するだけの場合はどうなりますか?

繰り返しますが、これは生成されたバイトコードとJava仮想マシンによって実行されるランタイム最適化に依存します。コンパイラ/ JVMがメソッド呼び出しをインライン化できる場合、実行時に新しいスタックフレームを作成するオーバーヘッドを回避するために最適化を実行します。

プログラミングのベストプラクティスは何ですか?

時期尚早の最適化を回避します。ベストプラクティスは、読みやすく適切に設計されたコードを記述してから、アプリケーションのパフォーマンスホットスポットに合わせて最適化することです。


ベンチマークする良い方法は何ですか?ダウンロードできるソフトウェアはありますか、それとも最初と最後にSystem.nanoTime()を使用して、違いを出力するだけですか?
jhlu87 2011年

System.nanoTime()またはSystem.currentTimeMillis、プロファイリングを行うには不十分な方法です。このStackoverflowの質問の回答からプロファイラーのリストを取得できます。VisualVMがJDKに付属しているので、VisualVMをお勧めします。
Vineet Reynolds 2011

2
@ jhlu87:メソッド呼び出しのオーバーヘッドを正確に見積もるのは非常に難しいと思います。マイクロベンチマークは正しく理解するのが非常に難しく、それでも一般的に物事の壮大な計画ではあまり役に立ちません。これを読んでください。
ColinD 2011年

@VineetReynoldsリンクされた質問は死んでいます。
dim8 2016

19

最適化について他の誰もが言ったことは絶対に真実です。

パフォーマンスの観点から、メソッドをインライン化する理由はありません。パフォーマンスの問題である場合は、JVMのJITがインライン化します。Javaでは、メソッド呼び出しは無料に非常に近いため、考える価値はありません。

そうは言っても、ここには別の問題があります。すなわち、あるoverrideable方法(すなわち、ないものを呼び出すのは悪いプログラミング手法finalstaticまたはprivateコンストラクタから)を。(「相続のための設計と文書化、またはそれを禁止する」というタイトルの項目の有効なJava、第2版、89ページ)

誰かが次のようなコードですべてのパブリックメソッドをオーバーライドするBinarySearchTreecalledのサブクラスを追加するとどうなりますか?LoggingBinarySearchTree

public void clear(){
  this.callLog.addCall("clear");
  super.clear();
}

そうして LoggingBinarySearchTree構築可能になることはありません!問題は、コンストラクターが実行this.callLogされているnullときですが、呼び出されるのはオーバーライドされたものであり、次のようになります。BinarySearchTreeclearNullPointerExceptionます。

ここではJavaとC ++が異なることに注意してください。C++では、を呼び出すスーパークラスコンストラクター virtualメソッドは、オーバーライドされたものではなく、スーパークラスで定義されたものを呼び出すことになります。2つの言語を切り替える人々は、これを忘れることがあります。

それを考えると、コンストラクターから呼び出されたときclearメソッドをインライン化する方がおそらくクリーンだと思いますが、一般にJavaでは、先に進んで必要なすべてのメソッド呼び出しを行う必要があります。


1
彼がコーディングスタイルのヒントを求めていたのではなく、メソッド呼び出しが高価かどうかを知っていたと思います
Asaf Mesika 2013

3
彼は明確に「プログラミングのベストプラクティスは何ですか?」と尋ねました。-ベストプラクティスの問題として、これは完全に関連しています。
ダニエルマーティン

2
最後の文だけを取ると、この質問の文脈が完全に失われます。メソッド呼び出しにはコストがかかるため、彼は大きなメソッドを多くの小さなメソッドに分解するのに費用がかかるかどうかを知りたがっています。コンストラクターから非finalメソッドを呼び出すためのアンチパターンを説明する回答を追加しても、全体として彼の質問に対する回答としてカウントされません。ああ、「メソッド呼び出しはどれくらい高いか」という質問のタイトルをチェックしてください
Asaf Mesika 2015年

6

絶対にそのままにしておきます。clear()ロジックを変更するとどうなりますか?2行のコードをコピーしたすべての場所を見つけることは実用的ではありません。


4

一般的に言えば(そして初心者としてこれは常に意味します!)あなたが考えているようなマイクロ最適化を決してしてはいけません。このようなことよりも、コードの可読性を常に優先します。

どうして?コンパイラ/ホットスポットは、この種の最適化をその場で行うため、さらに多くのことを行います。どちらかといえば、これらの種類の線に沿って最適化を試みた場合(この場合はそうではありませんが)、おそらく物事が遅くなります。ホットスポットは一般的なプログラミングイディオムを理解しています。自分でその最適化を実行しようとすると、おそらく何をしようとしているのか理解できないため、最適化できません。

メンテナンスコストもはるかに高くなります。コードを繰り返し始めると、維持するのにはるかに多くの労力が必要になります。これは、おそらくあなたが思っているよりもはるかに面倒です!

余談ですが、コーディングライフの中で、低レベルの最適化を行う必要があるポイントに到達する可能性がありますが、それらのポイントに到達すると、いつ来るかが確実にわかります。そうでない場合は、必要に応じていつでも戻って後で最適化できます。


3

ベストプラクティスは、2回測定し、1回カットすることです。

時間の最適化を無駄にすると、二度と元に戻すことはできません。(最初にそれを測定し、最適化する価値があるかどうかを自問してください。実際にどれだけの時間を節約できますか?)

この場合、Java VMは、おそらくすでに話している最適化を実行しています。


3

メソッド呼び出しのコストは、スタックフレームの作成(および破棄)と、メソッドに値を渡す必要がある場合の追加のバイトコード式です。


1

私が従うパターンは、問題のこのメソッドが次のいずれかを満たすかどうかです。

  • このクラスの外でこのメソッドを利用できるようにすると便利ですか?
  • このメソッドを他のメソッドで使用できるようにすると便利ですか?
  • 必要になるたびにこれを書き直すのはイライラしますか?
  • いくつかのパラメータを使用して、メソッドの多様性を高めることができますか?

上記のいずれかに該当する場合は、独自の方法でラップする必要があります。


これらの質問をせずに、darnコードを独自のメソッドに配置する方が簡単です。
アラファンギオン2011年

2
質問者は、メソッド呼び出しの粒度がどうあるべきかを知りたがっています。使用できる場合は、整数をインクリメントするメソッドを作成する必要はありませんi++;
Peaches491 2011年

実際、メソッドを再利用する機会がなくても、メソッドを作成することには多くの価値があります。コードのブロックに名前を付けて全体の構造を表示するだけで、それ自体が大きなメリットになります。
ジョフリー

1

clear()読みやすくなる場合は、メソッドを保持してください。保守不可能なコードがあると、よりコストがかかります。


1

最適化コンパイラは通常、これらの「余分な」操作から冗長性を取り除くというかなり良い仕事をします。多くの場合、「最適化された」コードと、単純に希望どおりに記述され、最適化コンパイラーを実行するコードとの違いはありません。つまり、最適化コンパイラは通常、あなたと同じようにうまく機能し、ソースコードの劣化を引き起こすことなくそれを実行します。実際、コンパイラは最適化を行うときに多くのことを考慮するため、多くの場合、「手動で最適化された」コードの効率は低下します。コードは読みやすい形式のままにしておき、後で最適化するまで心配しないでください。

「時期尚早の最適化はすべての悪の根源です。」-ドナルド・クヌース


0

メソッドの呼び出しについてはそれほど心配しませんが、メソッドのロジックについては心配します。それが重要なシステムであり、システムが「高速」である必要がある場合は、実行に時間がかかるコードの最適化を検討します。


0

現代のコンピュータのメモリを考えると、これは非常に安価です。コードをメソッドに分割して、誰かが何が起こっているのかをすばやく読み取れるようにすることをお勧めします。エラーが数行の本文を持つ単一のメソッドに制限されている場合は、コード内のエラーを絞り込むのにも役立ちます。


0

他の人が言っているように、コンパイラがあなたのためにそれを最適化するので、メソッド呼び出しのコストは簡単です。

とはいえ、コンストラクターからインスタンスメソッドへのメソッド呼び出しを行うことには危険があります。コンストラクターによってまだ開始されていないインスタンス変数を使用しようとする可能性があるように、後でインスタンスメソッドを更新するリスクがあります。つまり、必ずしもコンストラクターから構築アクティビティを分離する必要はありません。

もう1つの質問-clear()メソッドはルートをEMPTYに設定します。これは、オブジェクトの作成時に初期化されます。次にノードをEMPTYに追加してから、clear()を呼び出すと、ルートノードはリセットされません。これはあなたが望む行動ですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.