質問

私の教授が小さなプログラムで非公式のベンチマークを実行したところ、Java の時間は次のようになりました。最初の実行では 1.7 秒、その後の実行では 0.8 秒でした。

  • これはもっぱらランタイム環境を動作環境にロードしたことが原因なのでしょうか?

    または

  • Java によるコードの最適化と、その最適化の結果の保存 (申し訳ありませんが、専門用語がわかりません) の影響を受けていますか?

役に立ちましたか?

解決

投稿者が見たパフォーマンスの違いは、JRE をメモリに取り込むディスク遅延によって引き起こされている可能性が高いということに私も同意します。Just In Time コンパイラー (JIT) は、小さなアプリケーションのパフォーマンスには影響を与えません。

Java 1.6u10 (http://download.java.net/jdk6/) バックグラウンド プロセスでランタイム JAR にアクセスし (Java が実行されていない場合でも)、データをディスク キャッシュに保持します。これにより、起動時間が大幅に短縮されます (これはデスクトップ アプリにとっては大きなメリットですが、サーバー側アプリにとってはおそらくわずかな価値しかありません)。

大規模で長時間実行されるアプリケーションでは、JIT は時間の経過とともに大きな違いをもたらします。ただし、JIT が開始して最適化するのに十分な統計を蓄積するのに必要な時間 (5 ~ 10 秒) は、全体の寿命と比較すると非常に短いです。アプリケーションの実行 (ほとんどの場合、何ヶ月も実行されます)。JIT 結果の保存と復元は学術的な興味深い作業ではありますが、実際の改善はそれほど大きくありません (そのため、JIT チームはメモリ キャッシュ ミスを最小限に抑えるための GC 戦略などに重点を置いています...)。

ランタイム クラスのプリコンパイルは、デスクトップ アプリケーションにかなり役立ちます (前述の 6u10 ディスク キャッシュのプリロードも同様です)。

他のヒント

さて、それを読んだ場所を見つけました。これはすべて「Learning Java」(O'Reilly 2005) からの抜粋です。

従来の JIT コンパイルの問題は、コードの最適化に時間がかかることです。そのため、JIT コンパイラーは適切な結果を生成できますが、アプリケーションの起動時に大幅な遅延が発生する可能性があります。これは通常、長時間実行されるサーバー側アプリケーションにとっては問題ではありませんが、クライアント側ソフトウェアや、機能が限られた小型デバイスで実行されるアプリケーションにとっては深刻な問題です。これに対処するために、HotSpot と呼ばれる Sun のコンパイラ テクノロジは、アダプティブ コンパイルと呼ばれるトリックを使用します。プログラムが実際にどのようなことに時間を費やしているかを調べてみると、コードの比較的小さな部分を繰り返し実行することにほぼすべての時間を費やしていることがわかります。繰り返し実行されるコードの塊はプログラム全体のほんの一部にすぎませんが、その動作がプログラムの全体的なパフォーマンスを決定します。また、アダプティブ コンパイルを使用すると、静的にコンパイルされた言語では単純に実行できない新しい種類の最適化を Java ランタイムで利用できるため、場合によっては Java コードが C/C++ よりも高速に実行できると主張されます。

この事実を利用するために、HotSpot は通常の Java バイトコード インタプリタとして開始されますが、次のような違いがあります。実行中のコードを測定 (プロファイリング) して、どの部分が繰り返し実行されているかを確認します。コードのどの部分がパフォーマンスにとって重要であるかを認識すると、HotSpot はそれらのセクションを最適なネイティブ マシン コードにコンパイルします。プログラムのごく一部のみをマシンコードにコンパイルするため、それらの部分の最適化に必要な時間をかける余裕があります。プログラムの残りの部分はコンパイルする必要がなく、解釈するだけでメモリと時間を節約できます。実際、Sun のデフォルトの Java VM は、次の 2 つのモードのいずれかで実行できます。クライアントとサーバーは、起動時間の短縮とメモリの節約を重視するか、パフォーマンスを最大限に高めるかを決定します。

この時点で当然の疑問は、アプリケーションをシャットダウンするたびに、なぜこの優れたプロファイリング情報をすべて破棄するのかということです。Sun は、最適化された形式で永続的に保存される読み取り専用の共有クラスを使用することで、Java 5.0 のリリースでこの話題に部分的に切り込みました。これにより、特定のマシン上で多くの Java アプリケーションを実行する際の起動時間とオーバーヘッドの両方が大幅に削減されます。これを行うためのテクノロジーは複雑ですが、アイデアはシンプルです。プログラムの高速化が必要な部分を最適化すれば、残りの部分については心配する必要はありません。

Java 5.0 以来、Sun がどこまでこれに取り組んできたのか気になるところです。

プログラム呼び出し間の統計的な使用状況データを保存する、広く使用されている仮想マシンを私は知りません。しかし、将来の研究にとって興味深い可能性であることは確かです。

表示されている内容は、ほぼ確実にディスク キャッシュが原因です。

おそらくディスク キャッシュの結果であることに同意します。

参考までに、IBM Java 6 VM には、Ahead-of-Time コンパイラー (AOT) が含まれています。コードは JIT が生成するものほど最適化されていませんが、VM 間で保存されており、ある種の永続的な共有メモリがあると考えられます。その主な利点は、起動時のパフォーマンスが向上することです。IBM VM はデフォルトで、メソッドが 1000 回呼び出された後に JIT を実行します。VM の起動中にメソッドが 1000 回呼び出されることを知っている場合 (次のような一般的に使用されるメソッドを考えてください) java.lang.String.equals(...) ) の場合、それを AOT キャッシュに保存すると、実行時のコンパイルに時間を無駄にしなくて済みます。

ベンチマークがどのように実行されたかを説明する必要があります。特にどの時点で時間を計測し始めるか。

JVM の起動時間を含める場合 (ユーザー エクスペリエンスのベンチマークには役立ちますが、Java コードの最適化にはあまり役に立ちません)、ファイル システムのキャッシュの影響であるか、「Java クラス データ共有」と呼ばれる機能が原因である可能性があります。

太陽の場合:

http://java.sun.com/j2se/1.5.0/docs/guide/vm/class-data-sharing.html

これは、JVM がランタイム クラスの準備されたイメージをファイルに保存し、次回の起動時にそれらをより迅速にロード (および共有) できるようにするオプションです。これは、Sun JVM で -Xshare:on または -Xshare:off を使用して制御できます。デフォルトは -Xshare:auto で、共有クラス イメージが存在する場合はそれを読み込みます。存在しない場合は、ディレクトリが書き込み可能であれば最初の起動時に書き込みます。

IBM Java 5 では、これはさらに強力です。

http://www.ibm.com/developerworks/java/library/j-ibmjava4/

JIT 統計を保存している主流の JVM を私は知りません。

Java JVM (実際には、JVM のさまざまな実装によって変更される可能性があります) は、最初の起動時にバイト コードを解釈します。コードが十分な回数実行されることを検出すると、コードをネイティブ マシン言語に JIT して、より高速に実行されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top