3

TPTP を使用して、実行速度の遅い Java コードのプロファイリングを行っているときに、興味深いものを見つけました。私のプライベート プロパティ ゲッターの 1 つは、実行時間分析の結果で大きな Base Time 値を持っています。公平を期すために、このプロパティは何度も呼び出されますが、このようなプロパティに非常に時間がかかるとは思いもしませんでした。

public class MyClass{
    private int m_myValue;    
    public int GetMyValue(){
        return m_myValue;
    }
}

わかりましたので、クラスには明らかにもっと多くのものがありますが、ご覧のとおり、getter が呼び出されたときに他に何も起こっていません (int を返すだけです)。あなたのためのいくつかの数字:

  • 実行の呼び出しの約 30% は getter にあります (これを減らすために取り組んでいます)
  • 実行の基本時間の約 25% がこのゲッターで費やされます
  • 平均ベース時間は 0.000175 秒です

比較のために、このゲッターを使用する別のクラスに別のメソッドがあります。

private boolean FasterMethod(MyClass instance, int value){
    return instance.GetMyValue() > m_localInt - value;
}

これは、平均ベース時間が 0.000018 秒 (1 桁低い) とはるかに短くなっています。

ここでの取引は何ですか?私が理解していないもの、または欠けているものがあると思います:

  1. ローカルプリミティブを返すのは、計算された値を返すよりも本当に時間がかかりますか?
  2. Base Time 以外のメトリックを確認する必要がありますか?
  3. これらの結果は誤解を招くものですか? 他のプロファイリング ツールを検討する必要がありますか?

編集 1:以下のいくつかの提案に基づいて、メソッドを最終としてマークし、テストを再実行しましたが、同じ結果が得られました。

編集 2:パフォーマンス テストを再実行するために、YourKit のデモ バージョンをインストールしました。YourKit の結果は、私が期待していたものにかなり近くなりました。私は引き続き YourKit をテストし、見つけたことを報告します。

編集 3: YourKit に変更すると、問題が解決したようです。YourKit を使用して、コード内の実際のスロー ポイントを特定できました。以下にいくつかの優れたコメントと投稿があります (適切に賛成票を投じました) が、YourKit を「正しい」と提案した最初の人を受け入れます。(私はいかなる方法でも YourKit と提携していません / YMMV)

4

4 に答える 4

6

可能であれば、別のプロファイラーを使用してみてください(Netbeansのものが適切に機能します)。コードの設定によっては、これを行うのが難しい場合があります。

他の多くのツールと同様に、プロファイラーが異なれば情報も異なる可能性があります。

ローカルプリミティブを返すことは、計算値を返すことよりも本当に時間がかかりますか?

インスタンス変数を返すには、ローカル変数を返すよりも時間がかかります(VMに依存します)。使用しているゲッターは単純なので、インライン化する必要があります。これにより、パブリックインスタンス変数にアクセスするのと同じくらい速くなります(これも、ローカル変数にアクセスするよりも遅くなります)。

ただし、ローカル値はありません(クラスではなくメソッドでのローカルの意味)。

「ローカル」とはどういう意味ですか?

基準時間以外のメトリックを確認する必要がありますか?

私はEclipseツールを使用したことがないので、どのように機能するかわかりません...トレースまたはサンプリングプロファイラーの場合は違いが生じる可能性があります(2つはこのようなものに対して異なる結果をもたらす可能性があります)。

これらの結果は誤解を招くものであり、他のプロファイリングツールを検討する必要がありますか?

結果が同じかどうかを確認するために、別のツールを検討します。

コメントに基づいて編集:

サンプリングプロファイラーの場合、基本的に、プログラムがどこにあるかを確認するために、すべての「n時間単位」でプログラムがサンプリングされます。一方のメソッドをもう一方のメソッドよりも多く呼び出している場合は、より多く呼び出されているメソッド内にあると表示されます(そのメソッドが実行されている可能性が高くなります)。

トレースプロファイラーは、プログラムにコードを追加し(インストルメンテーションと呼ばれるプロセス)、基本的に何が起こっているかをログに記録します。

プロファイラーのトレースは低速ですが、より正確です。また、プログラムを変更する必要があり(インストルメンテーションプロセス)、バグが発生する可能性があります(発生したことを聞いたことがありません...しかし、少なくとも開発中はそうなると確信しています)。プロファイラー)。

プロファイラーのサンプリングは高速ですが、精度は低くなります(コード行が実行される頻度を推測するだけです)。

したがって、Eclipseがサンプリングプロファイラーを使用している場合は、奇妙な動作と見なされるものを確認できます。トレースプロファイラーに変更すると、より正確な結果が表示されます。

Eclipseがトレースプロファイラーを使用している場合、プロファイラーの変更でも同じ結果が表示されるはずです(ただし、新しいプロファイラーを使用すると、何が起こっているかがより明確になる場合があります)。

于 2009-03-27T16:12:48.727 に答える
3

それは少し誤解を招くように聞こえます-おそらくプロファイラーはいくつかの最適化を削除していますか?

キックのためだけに、メソッドをfinalにしてみてください。これにより、インライン化が容易になります。これが、プロパティとFasterMethodの違いかもしれません。実際の使用では、HotSpotは、仮想メソッドが最初にオーバーライドされるまで(IIRC)、仮想メソッドもインライン化します。

編集:ブライアンのコメントへの応答:はい、通常、最終的なものを作成してもパフォーマンスには役立たない場合があります(ただし、デザインの観点からは良いことかもしれません:)オーバーライドされているかどうか。私は、このプロファイラーがそれを台無しにした可能性があることを示唆していました。

編集:HotSpotがまだ拡張されていないクラス(またはオーバーライドされていないメソッド)の最適化を「元に戻す」方法を再現することができました。これは、クライアントよりもサーバーVMで行うのが困難でしたが、私はそれを行いました:)

public class Test
{
    public static void main(String[] args)
        throws Exception
    {
        final long iterations = 1000000000L;
        Base b = new Base();
        // Warm up Hotspot
        time(b, 1000);

        // Before we load Derived
        time(b, iterations);

        // Load Derived and use it quickly
        // (Just loading is enough to make the client VM
        // undo its optimizations; the server VM needs more effort)
        Base d = (Base) Class.forName("Derived").newInstance();
        time(d, 1);

        // Time it again with Base
        time(b, iterations);
    }

    private static void time(Base b, long iterations)
    {
        long total = 0;
        long start = System.currentTimeMillis();
        for (long i = 0; i < iterations; i++)
        {
            total += b.getValue();
        }
        long end = System.currentTimeMillis();
        System.out.println("Time: " + (end-start));
        System.out.println("Total: " + total);
    }
}

class Base
{
    public int getValue() { return 1; }
}

class Derived extends Base
{
    @Override
    public int getValue() { return 2; }
}
于 2009-03-27T16:08:08.813 に答える
2

それは非常に奇妙に聞こえます。誤ってオーバーライドメソッドを呼び出しているのではありませんか?

YourKitのデモ版をダウンロードしたくなるでしょう。設定するのは簡単で、実際に何が起こっているのかを示す必要があります。TPTPとYourKitの両方が一致する場合は、何か奇妙なことが起こっています(そして、それはあまり役に立たないことを私は知っています!)

于 2009-03-27T16:10:39.073 に答える
0

これらの種類のメソッドのパフォーマンスに大きな違いをもたらしていたもの (これはある程度歴史的なものかもしれませんが) は、呼び出し元のメソッドのサイズが問題になる可能性があるということです。HotSpot (および深刻なライバル) は、喜んで小さなメソッドをインライン化します (同期化/try-finally で詰まる場合もあります)。ただし、呼び出しメソッドが大きい場合は、そうでない場合があります。これは特に、古いバージョンの HotSpot C1/client で問題となりました。これは、非常に悪いレジスタ割り当てアルゴリズムを持っていました (現在は、非常に優れた高速なアルゴリズムを備えています)。

于 2009-03-27T18:17:55.617 に答える