Java仮想マシン(JVM)では、Java遅延読み込みプロセスを使用してクラスを読み込むため、ウォームアップが必要になる可能性があることを確認します。メイントランザクションを開始する前にオブジェクトが初期化されること私はC++開発者であり、同様の要件に対処する必要はありませんでした。
ただし、理解できない部分は次のとおりです。
たとえば、ソケットを介してメッセージを受信することが期待されるアプリケーションを考えてみてください。トランザクションは、新しい注文、注文の変更、注文のキャンセル、またはトランザクションの確認です。
アプリケーションは高周波取引(HFT)に関するものであるため、パフォーマンスが非常に重要であることに注意してください。
コードのどの部分をウォームアップする必要がありますか?
通常、何もする必要はありません。ただし、低遅延アプリケーションの場合は、システムのクリティカルパスをウォームアップする必要があります。ユニットテストが必要なので、起動時にそれらを実行してコードをウォームアップすることをお勧めします。
コードがウォームアップされても、CPUキャッシュも同様にウォーム状態を維持する必要があります。ブロック操作の後、パフォーマンスが大幅に低下することがわかります。ネットワークIO、最大50マイクロ秒。通常、これは問題ではありませんが、ほとんどの場合に50マイクロ秒未満にとどまる場合、ほとんどの場合これは問題になります。
注:ウォームアップを使用すると、エスケープ分析でいくつかのオブジェクトをスタックに配置できます。これは、そのようなオブジェクトを最適化する必要がないことを意味します。コードを最適化する前に、アプリケーションのメモリプロファイルを作成することをお勧めします。
コードの一部をウォームアップしても、どれくらいの時間ウォームアップしたままになります(この用語は、クラスオブジェクトがメモリ内にとどまる時間を意味すると仮定しています)。
時間制限はありません。 JItが、コードの最適化時に行った仮定が間違っているかどうかを検出するかどうかに依存します。
イベントを受信するたびに作成する必要があるオブジェクトがある場合、どのように役立ちますか?
低遅延または高パフォーマンスが必要な場合は、できるだけ少ないオブジェクトを作成する必要があります。 300 KB /秒未満の生産を目指しています。この割り当て率を使用すると、1日1回マイナーコレクションを行うのに十分な大きさのEdenスペースを確保できます。
例として、ソケットを介してメッセージを受信することが期待されるアプリケーションを考えてみてください。トランザクションは、新規注文、注文の変更、注文のキャンセル、またはトランザクションの確認です。
オブジェクトを可能な限り再利用することをお勧めしますが、割り当て予算内であれば、心配する価値はないかもしれません。
アプリケーションは高周波取引(HFT)に関するものであるため、パフォーマンスが非常に重要であることに注意してください。
さまざまな投資銀行やヘッジファンドのHFTシステムに使用されるオープンソースソフトウェアに興味があるかもしれません。
私の本番アプリケーションは高頻度取引に使用されており、レイテンシーのすべてが問題になる可能性があります。起動時にアプリケーションをウォームアップしないと、数ミリ秒の高遅延が発生することは明らかです。
https://github.com/OpenHFT/Java-Thread-Affinity に興味があるかもしれません。このライブラリは重要なスレッドのスケジューリングのジッターを減らすのに役立ちます。
また、最適化された方法で動作するには、ウォームアップを必要とするコードの重要なセクションを少なくとも12K回(偽のメッセージで)実行する必要があると言われています。なぜ、どのように機能しますか?
コードはバックグラウンドスレッドを使用してコンパイルされます。これは、メソッドがネイティブコードへのコンパイルに適格であっても、コンパイラが既にかなり忙しいときに起動時にそうすることを意味しないことを意味します。 12Kは不合理ではありませんが、それより高い可能性があります。
温暖化とは、JVMが解釈を停止し、ネイティブにコンパイルするのに十分な回数コードを実行することを指します(少なくとも初めて)。通常、それはあなたがしたくないことです。その理由は、JVMがコード生成中に使用する問題のコードに関する統計を収集するためです(プロファイルの最適化に似ています)。そのため、問題のコードチャンクが、実際のデータとは異なるプロパティを持つ偽のデータで「温められている」場合、パフォーマンスが低下する可能性があります。
編集:JVMはプログラム全体の静的分析を実行できないため(アプリケーションによってどのコードがロードされるかを知ることはできません)、代わりに収集した統計から型について推測することができます。例として、正確な呼び出し場所で(C++で話す)仮想関数を呼び出し、すべての型の実装が同じであると判断した場合、呼び出しは直接呼び出し(またはインライン化)にプロモートされます。後でその仮定が間違っていることが証明された場合、適切に動作するには古いコードを「コンパイル解除」する必要があります。 AFAIK HotSpotは、呼び出しサイトを、単相(単一実装)、双相(厳密に2 .. if(imp1-type){imp1} else {imp2}に変換)および完全な多相..virtualディスパッチに分類します。
また、再コンパイルが発生する別のケースがあります。階層化されたコンパイルがある場合。最初の層は、優れたコードを生成するために費やす時間が短くなり、メソッドが「十分にホット」な場合、より高価なコンパイル時コードジェネレーターが起動します。
ウォームアップはめったにありません必須。 JITのウォームアップ時間が結果を歪めないようにするために、たとえばパフォーマンステストを行う場合に関連します。
通常の製品コードでは、ウォームアップ用のコードはほとんど見られません。 JITは通常の処理中にウォームアップするため、そのためだけに追加のコードを導入する利点はほとんどありません。最悪の場合、バグが発生し、余分な開発時間を費やし、パフォーマンスを損なうことさえあります。
何らかのウォームアップが必要であることが確実にわかっていない限り、心配する必要はありません。あなたが説明したサンプルアプリケーションは確かにそれを必要としません。
JVMにウォームアップが必要な理由
最新の(J)VMは、実行時に最も頻繁に使用されるコードとその使用方法に関する統計を収集します。 1つ(数千ではないにしても数百)の例は、実装のみを行う(C++の専門用語での)仮想関数の呼び出しの最適化です。これらの統計は、定義上、実行時にのみ収集できます。
クラスのロード自体もウォームアップの一部ですが、これらのクラス内でコードが実行される前に明らかに自動的に行われるため、心配する必要はあまりありません。
コードのどの部分をウォームアップする必要がありますか?
アプリケーションのパフォーマンスにとって重要な部分。重要な部分は、通常の使用時に使用されるのと同じ方法で「ウォームアップ」することです。そうしないと、誤った最適化が行われます(後で元に戻されます)。
コードの一部をウォームアップしても、どのくらいの時間ウォームアップしたままになります(この用語は、クラスオブジェクトがメモリ内にとどまる時間を意味すると仮定しています)?
これは基本的に、JITコンパイラが実行とパフォーマンスを常に監視していると言うのは本当に難しいです。しきい値に達すると、最適化が試行されます。その後、パフォーマンスの監視を続け、最適化が実際に役立つことを確認します。そうでない場合、コードが最適化されない可能性があります。また、新しいクラスのロードなど、最適化を無効にする事態が発生する可能性があります。少なくともstackoverflowの答えに基づいていないこれらのことを予測できないと思いますが、JITが何をしているかを伝えるツールがあります: https://github.com/AdoptOpenJDK/jitwatch
イベントを受け取るたびに作成する必要があるオブジェクトがある場合、どのように役立ちますか。
簡単な例の1つは、メソッド内にオブジェクトを作成することです。参照はメソッドのスコープを離れるため、これらのオブジェクトはヒープに格納され、最終的にガベージコレクターによって収集されます。これらのオブジェクトを使用するコードが頻繁に使用される場合、これらのオブジェクトがこのメソッド内にのみ存在するまで、単一の大きなメソッドでインライン化され、認識を超えて並べ替えられる可能性があります。その時点で、それらはスタックに置かれ、メソッドが終了すると削除されます。これにより、大量のガベージコレクションを保存でき、ウォームアップ後にのみ発生します。
とはいえ、ウォーミングアップのために何か特別なことをする必要があるという考えには懐疑的です。アプリケーションを起動し、それを使用するだけで、JITコンパイラーは問題なく処理します。問題が発生した場合は、アプリケーションでJITが何をするか、その動作を微調整する方法、またはアプリケーションが最も利益を得るようにアプリケーションを作成する方法を学習します。
ウォームアップの必要性について実際に知っている唯一のケースはベンチマークです。そこを無視すると、ほとんど間違いのない偽の結果が得られるからです。
JIT
コンパイラに関するものです。これは、JVM
で実行時にバイトコードを最適化するために使用されます(javac
は、プラットフォームによって高度または積極的な最適化技術を使用できないため、バイトコードの独立した性質)
メッセージを処理するコードをウォームアップできます。実際、ほとんどの場合、特別なウォームアップサイクルでそれを行う必要はありません。アプリケーションに最初のメッセージの一部を開始して処理させるだけです-JVM
はコード実行を分析するために最善を尽くします。最適化を行います:)偽のサンプルを使用した手動のウォームアップはさらに悪い結果をもたらす
コードはある程度の時間後に最適化され、プログラムフローの何らかのイベントがコード状態を劣化させるまで最適化されます(JIT
コンパイラーはコードを再度最適化しようとします-このプロセスは終了しません)
短命のオブジェクトも最適化の対象になりますが、一般に、メッセージ処理の終身コードをより効率的にするのに役立つはずです
コードのどの部分をウォームアップする必要がありますか?
一般的に、この質問に対する答えはありません。それはあなたのアプリケーションに完全に依存します。
コードの一部をウォームアップしても、どのくらいの時間ウォームアップしたままになります(この用語は、クラスオブジェクトがメモリ内にとどまる時間を意味すると仮定しています)?
オブジェクトは、プログラムがオブジェクトへの参照を保持している限り、特別な弱参照の使用などがない限り、メモリ内に残ります。プログラムが何かを「参照する」時期を知ることは、一見思ったよりもわかりにくいかもしれませんが、それはJavaおよび努力の価値があるメモリ管理の基礎です。
イベントを受け取るたびに作成する必要があるオブジェクトがある場合、どのように役立ちますか。
これは、アプリケーションに完全に依存しています。一般的に答えはありません。
クラスロード、メモリ管理、パフォーマンス監視などを理解するには、Javaで学習して作業することをお勧めします。オブジェクトのインスタンス化にはある程度の時間を要し、一般にロードにはさらに時間がかかりますクラス(もちろん、通常ははるかに少ない頻度で行われます)通常、クラスがロードされると、プログラムの存続期間中はメモリ内に留まります-これは、 理解である必要があります。答えを得るだけではありません。
あなたがそれらをまだ知らないかどうかを学ぶためのテクニックもあります。一部のプログラムは、オブジェクトの「プール」を使用し、実際に必要になる前にインスタンス化し、必要が生じたら処理を行うために引き渡します。これにより、プログラムのタイムクリティカルな部分で、タイムクリティカルな期間中にインスタンス化に費やされる時間を回避できます。プールはオブジェクトのコレクション(10?100?1000?10000?)を維持し、必要に応じてインスタンス化などを行います。ただし、プールの管理はプログラミングに多大な労力を要し、もちろん、プール内のオブジェクトでメモリを占有します。
ガベージコレクションをより頻繁にトリガーするのに十分なメモリを使い果たし、速度を上げようとしているシステムを遅くすることは完全に可能です。これが、「答えを得る」だけでなく、その仕組みを理解する必要がある理由です。
別の考慮事項-プログラムを高速化するために費やされた労力のほとんどは、不要なため無駄になっています。検討中のアプリケーションに関する広範な経験やシステムの測定がなければ、最適化がどこで(または))顕著になるかさえわかりません。遅延の病理学的なケースを回避するためのシステム/プログラム設計は有用であり、「最適化」の時間と労力をほとんどかけません。ほとんどの場合、必要なのはそれだけです。
-編集-ジャストインタイムコンパイルを学習および理解するもののリストに追加します。
私はいつも次のように描いていました。
あなたは(C++開発者)として、自動化されたjvm
のさまざまなビットをコンパイル/ホットロード/置換することによる反復アプローチを想像できます架空のアナログ)gcc -O0
、-O1
、-O2
、-O3
バリアント(必要に応じて元に戻す場合もあります)
これは厳密に何が起こるかではないと確信していますが、C++開発者にとって有用な類推かもしれません。
標準のjvmでは、スニペットがjitであると見なされるまでにかかる時間は、-XX:CompileThreshold
で設定されます。デフォルトは1500です。 (ソースとjvmのバージョンは異なりますが、jvm8の場合はそうだと思います)
さらに book Host Performace JIT Chapter(p59)で、JIT中に次の最適化が行われると述べています。
編集:
コメントに関して
1500は、JITにコードをネイティブにコンパイルして解釈を停止する必要があることを示唆するのに十分であると思います。同意しますか?
その単なるヒントかどうかはわかりませんが、openjdkはオープンソースなので、 globals.hpp#l3559@ver-a801bc33b08c (jdk8uの場合)のさまざまな制限と数値を見てみましょう
(私はjvm開発者ではありませんが、これは完全に間違った場所かもしれません)
コードをネイティブにコンパイルしても、必ずしも最適化されるとは限りません。
私の理解-真実;特に-Xcomp
(強制コンパイル)を意味する場合-この ブログ は、jvmがプロファイリングを実行できないことを示しています-したがって、最適化--Xmixed
(デフォルト)。
そのため、頻繁にアクセスされるネイティブコードをサンプリングして最適化するために、タイマーが作動します。このタイマー間隔を制御する方法を知っていますか?
私は本当に詳細を知りませんが、私がリンクしたgobals.hpp
は確かにいくつかの周波数間隔を定義します。