web-dev-qa-db-ja.com

javaメソッド呼び出しの費用

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

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個のメソッド呼び出しを行った場合はどうなりますか?プログラミングのベストプラクティスは何ですか?

76
jhlu87

実際のメソッドを呼び出すのではなく、clear()メソッドの2行をコンストラクターにコピーアンドペーストする方が最適でしょうか?

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

もしそうなら、それはどれほどの違いをもたらしますか?

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

コンストラクタがインスタンス変数を値に設定するだけで10個のメソッド呼び出しを行った場合はどうなりますか?

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

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

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

70
Vineet Reynolds

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

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

とはいえ、ここには別の問題があります。つまり、isからオーバーライド可能なメソッド(つまり、finalstatic、またはprivateではないメソッド)を呼び出すのは不適切なプログラミング手法です。コンストラクタ。 (有効なJava、第2版、89ページの「継承またはその他の禁止のための設計と文書化」というタイトルの項目)

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

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

すると、LoggingBinarySearchTreeは決して構築可能になりません!問題は、this.callLogは、nullコンストラクターが実行されている場合はBinarySearchTreeになりますが、呼び出されるclearはオーバーライドされ、NullPointerExceptionが取得されます。 。

JavaとC++はここで異なります:C++では、virtualメソッドを呼び出すスーパークラスコンストラクターは、オーバーライドされたメソッドではなく、スーパークラスで定義されたメソッドを呼び出します。 2つの言語の間でこれを忘れることがあります。

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

18
Daniel Martin

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

5
Marcelo

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

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

この場合、Java VMはおそらくあなたが話している最適化をすでに行っているでしょう。

3
Arafangion

一般的に言えば(そして初心者としては常にこれを意味します!)あなたが検討しているような最適化を決して行うべきではありません。このようなことよりも常にコードの可読性を優先してください。

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

また、メンテナンスコストがはるかに高くなります。コードの繰り返しを開始すると、メンテナンスにかなりの労力が費やされることになり、おそらくあなたが考えるよりもはるかに手間がかかるでしょう!

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

3
Michael Berry

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

2
Andreas_D

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

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

上記のいずれかが当てはまる場合は、独自のメソッドでラップする必要があります。

1
Peaches491

読みやすくする場合は、clear()メソッドを保持します。保守不能なコードを使用すると、費用がかかります。

1
Fabian Barney

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

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

1
Paul Sonier

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

0
Buhake Sindi

他の人が言ったように、メソッド呼び出しのコストは、コンパイラがあなたのために最適化するので、nへの些細なものです。

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

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

0
Matthew Flynn

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

0
adamjmarkham