TencentJDKローカライズされたCPUアーキテクチャは共有をサポートします

GIAC(GLOBAL INTERNET ARCHITECTURE CONFERENCE)は、インターネット技術とアーキテクチャに長い間焦点を当ててきた、可用性の高いアーキテクチャ技術コミュニティとmsupによって立ち上げられた、建築家、技術リーダー、ハイエンド技術実務家のための年次技術アーキテクチャ会議です。これは中国で最大の技術です。会議の1つ。

今年の総会で6番目のGIAC、進化するビッグデータアーキテクチャのJAVAトピック、TencentのシニアエンジニアであるFu Jie医師は、このトピックについて「TencentJDKローカリゼーションCPUアーキテクチャが共有をサポートする」を発行しました。以下はゲストスピーチの記録です。

親愛なるゲスト、みなさん、こんにちは!TencentJDKのローカライズされたCPUアーキテクチャサポートのトピックを皆さんと共有する機会を得られてとてもうれしく思います。TencentのJVMチームのJiefu(Fu Jie)です。中国科学アカデミーのコンピューティングテクノロジー研究所での大学院および博士課程の研究中にOpenJDKの研究開発に取り組み始めました。現在、OpenJDKコミュニティの委員を務めています。私は以前Loongsonで働いていて、OpenJDK mipsブランチのコア開発者でしたが、LoongsonでOpenJDKC2コンパイラを開発して実装しました。Tencentに入社した後、彼は主にビッグデータと機械学習の分野でのKonaJDKの調査と実践に専念しています。

今日は、最初にTencent Kona JDKについて簡単に紹介し、次に国内CPUアーキテクチャに対するJVMのサポートについて詳しく説明します。最後に、JVM実装に対するプロセッサメモリモデルの影響について説明します。

Tencent KonaJDKの紹介

Tencent Konaは、OpenJDKに基づいてTencentによって開発されたJDK製品です。これは、2019年に無料でオープンソースになり、長期サポート(LTS)を提供します。Konaのすべてのリリースは、TencentCloudと実際の内部実稼働環境によってテストおよび検証されています。ダウンロードして使用することを歓迎します。 

2020年3月にJDK14がリリースされたとき、当社は限られた数の国内企業であり、優れた貢献者/組織の世界的なリストに加わりました。OpenJDKグローバル貢献者リストは、OpenJDKに対する世界中のさまざまな企業または個人の貢献に関する信頼できる統計であり、JDKの新しいバージョンがリリースされたときにOracleによって発表されます。

TencentのJVMチーム(多くのOpenJDKコミュニティの作成者/コミッターを含む)は、Konaの開発と保守を担当しています。過去6か月で、チームはバグを修正するためにOpenJDKコミュニティに数十のパッチを提供しました。同時に、グースファクトリーはOpenJDKコミュニティにその膨大な生産負荷の経験と最先端の慣行を提供しています。今後も、よりオープンな姿勢でオープンソースを積極的に取り入れ、オープンソースへの貢献を続けていきます。

国内CPUアーキテクチャのJVMサポート

国内のCPUアーキテクチャに対するJVMのサポートの関連コンテンツをあなたと共有させてください。国内の加工業者は、中国のレターおよびクリエイティブ産業の発展の基盤です。現在、公式ディレクトリに入った国内のプロセッサは、ARM、MIPS、Alpha、X86の4つの主要なアーキテクチャに分けることができます。その中で、ARMはKunpengとFeitengによって表され、MIPSはLoongsonによって表され、AlphaはShenweiによって表され、X86はZhaoxinとHaiguangによって表されます。上記の4つのアーキテクチャでは、ARMとX86のOpenJDKコミュニティサポートを除いて、MIPSもAlphaもコミュニティサポートを備えておらず、すべて自分で開発および保守する必要があります。したがって、JVMがサポートするプロセッサ用のテクノロジを習得することは、外国の独占を打ち破り、国内のプロセッサの持続可能で健全な開発を促進するために非常に重要です。

OpenJDKのHotSpot仮想マシンは、世界で最も広く使用されている高性能Java仮想マシンです。マクロ設計レベルから、HotSpot仮想マシンは、クラスローダー、ランタイム、実行エンジン、およびガベージコレクターの4つのモジュールに分割できます。その中で、実行エンジンとプロセッサアーキテクチャのみが密接に関連しており、他の3つのモジュールはほとんどプラットフォームに依存していません(またはランタイムモジュールなどのオペレーティングシステムに部分的にのみ関連しています)。JVMの実行エンジンは、Javaバイトコードをプロセッサハードウェアでサポートされるマシン命令に変換する役割を果たします。したがって、このモジュールは主にCPUに関連しています。したがって、国産プロセッサのアーキテクチャに対するJVMのサポートは、基本的に、国産プロセッサでJVM実行エンジンを実現することです。では、JVMの実行エンジンをコードレベルでどのように実装する必要がありますか?

このページのPPTの左側には、HotSpot仮想マシンのソースコードの構成が示されています。基盤となるハードウェアおよびオペレーティングシステムとの相関関係に従って、HotSpotソースコードは、cpu(プロセッサ関連)、os(オペレーティングシステム関連)、os_cpu(プロセッサとオペレーティングシステムに同時に関連)、および共有(プラットフォームに依存しない)の4つのサブディレクトリに分割されます。PPTの中央部分には、各サブディレクトリによって実装される主な機能がリストされています。黄色でマークされている部分は、CPUアーキテクチャの関連部分です。PPTの右側では、ARMのaarch64プロセッサアーキテクチャを例として取り上げ、プロセッサアーキテクチャをサポートするためにJVMに必要なコードの量を定量的に分析します。CPUアーキテクチャに関連するコードの量は約64,000行で、残りの部分のコードの量は約70です。ミリオンライン。したがって、プロセッサアーキテクチャのサポートに必要なコードは8%未満です。アーキテクチャ関連のコードには、主にアセンブラ、インタプリタ、およびコンパイラのバックエンドが含まれます。さらに、Java言語はネイティブにマルチスレッドをサポートしているため、プロセッサは、同時プログラムの正確性を確保するために、アトミック操作とメモリバリアも提供する必要があります。以下では、アセンブラ、インタプリタ、コンパイラ、CPUアトミック操作、メモリバリアの観点から1つずつ拡張していきます。

アセンブラは、実装する必要がある最初のモジュールです。これは、インタプリタとコンパイラの構築が、インターフェイスを提供するためにアセンブラに依存しているためです。アセンブラは主にプロセッサハードウェアを抽象化してカプセル化し、プログラミングに必要なレジスタと命令を提供します。アセンブラは、いくつかのモジュールの中で最も単純な機能です。ただし、エンジニアリングの観点からは、最新のプロセッサは数千の命令をサポートしているため、アセンブラの実装タスクは困難であり、命令の形式とコーディングにエラーが発生する可能性があります。したがって、開発者はプロセッサの命令セットに精通し、コーディングプロセス中に注意する必要があります。

アセンブラが完了したら、すぐにインタプリタを実装する必要があります。全員に質問します。インタープリターをスキップして、HotSpot仮想マシンのコンパイラーを直接実装できますか?一部の人々は、インタープリターのパフォーマンスが低すぎると考えており、CPUアーキテクチャーに対するJVMサポートのワークロードを減らすためにインタープリターモジュールを削除したいと考えています。答えは否定的です。HotSpot仮想マシンは、インタープリターの機能に依存する必要があります。まず第一に、いくつかの特別なJavaメソッド(大きなサイズなど)の場合、コンパイラーはコンパイルを拒否し、インタープリターによってのみ解釈および実行できます。次に、HotSpotのコンパイラ、特にC2コンパイラは、特定の仮定に基づいて根本的なコンパイルの最適化を広範囲に使用します。ただし、これらの仮定は常に正しいとは限りません。失敗すると、仮想マシンはコンパイルと実行からインタープリターにフォールバックして実行を続行する必要があります。最後に、高速な起動と応答が必要な特定のシナリオでは、コンパイルしてから実行するよりも、直接解釈して実行する方がよい場合があります。したがって、通訳者の構築と支援が必要です。

HotSpotのインタープリターは、高性能のテンプレートベースのインタープリターです。いわゆる「テンプレート」は、Javaバイトコードのセマンティック関数を実装するために使用される一連のアセンブリ命令です。このページのPPTは、addメソッドがjavacによって4つのバイトコードにコンパイルされ、解釈されて実行される方法を示しています。解釈と実行は、実際には、プログラムの制御フローに従って、バイトコードの対応するテンプレート内の一連の命令を1つずつ実行するプロセスです。PPTの右側には、整数加算iaddバイトコードのインタープリターテンプレートが表示されます。上記の黄色の破線のボックス内のマシン命令は、オペランドをフェッチするために使用されます。下の黄色の破線のボックス内のマシン命令は、実行を続行するために次のバイトコードに対応するテンプレートにジャンプするために使用されます。真ん中のadd命令は、iaddバイトコードのセマンティクスを実現するために使用されます。インタプリタのテンプレートはすべて固定パターンに従います。つまり、最初にオペランドを取得してから実行し、最後に次のテンプレートにジャンプして実行を続行します。

インタプリタが正常にデバッグされた後、コンパイラのサポートを開始できます。コンパイラのサポートが最も難しく、デバッグサイクルが最も長くなります。HotSpotでは、C1とC2の2つのコンパイラが設計されています。C1コンパイラは高速にコンパイルされますが、生成されるコードの品質は高くありません。高速の起動と応答が必要なシナリオに適しているため、クライアントバージョンコンパイラとも呼ばれます。C2コンパイラで生成されるコードは高品質ですが、コンパイル速度が遅く、長時間繰り返し実行する必要のあるサービスアプリケーションに適しているため、サーバーバージョンコンパイラとも呼ばれます。C1と比較して、C2はますます根本的なコンパイラ最適化アルゴリズムを使用するため、C2はC1よりも複雑です。C1とC2の構造には多くの類似点があります。より複雑なC2を例として取り上げて、JVMで新しいCPUアーキテクチャをサポートするコンパイラを実装する方法を示しましょう。

このページのPPTは、C2コンパイラ構築の原理を示しています。コンパイラの移植の難しさを軽減するために、C2はプラットフォームに依存しないものとプラットフォームに依存するものの2つの部分に分けられます。プラットフォームに依存しないコードはすべてのプロセッサアーキテクチャに適用可能であり、プラットフォーム関連の部分のコードのみを移植してプロセッサアーキテクチャに適合させる必要があります。さらに、コードのプラットフォーム関連部分を手動で作成する作業負荷を軽減するために、C2はADLコンパイラを使用して、プロセッサアーキテクチャに関連するコードを自動的に生成します。ADLは、OpenJDKオープンソースコードに埋め込まれているアーキテクチャ記述言語であるArchitecture DescriptionLanguageの英語の略語です。ADLコンパイラは、アーキテクチャ記述ファイル(aarch64.adなどの* .adのサフィックスを持つファイル)を解析することによってC2コードを生成します。したがって、新しいプロセッサアーキテクチャでC2をサポートするための作業のほとんどは、プロセッサのアーキテクチャ記述ファイルを正しく書き込むことです。アーキテクチャ記述ファイルには、主に、レジスタ記述、オペランド記述、および命令セット記述の3つの側面が含まれます。

このページのPPTは、Aarch64を例として使用したレジスタ記述の例を示しています。レジスタの説明には、通常、汎用レジスタ、浮動小数点レジスタ、およびベクトルレジスタが含まれます。32ビットオペレーティングシステムとの互換性を保つために、32ビット長がレジスタ記述の基本記述単位として使用されます。たとえば、PPTの上部にあるR1とR1_Hは、一緒に64ビットのR1レジスタを表します。PPTの下半分のV0、V_H、V_J、およびV_Kは、128ビット長のV0浮動小数点レジスタを表します。

このページのPPTは、オペランドの説明の例を示しています。オペランドは、プロセッサによって直接サポートされるデータのタイプを記述します。これには、イミディエートオペランド、レジスタオペランド、メモリオペランドの3つのカテゴリが含まれます。各主要カテゴリでは、文字、整数、浮動小数点、ポインタなどの特定のサブタイプにさらに細分化されます。

このページのPPTは、命令の説明の例を示しています。命令の説明は、プロセッサハードウェアがサポートする命令を説明するだけでなく、C2コンパイラの命令の選択と生成にも影響を与えるため、コンパイラのパフォーマンスに影響を与えることに注意してください。実際、アーキテクチャファイルの命令の説明では、CPUマシンの命令を使用して、コンパイラの中間コード表現と一致させる方法を指定しています。PPTの左側にあるaddI_reg_regの命令の説明は、PPTの右の図に示すように、コンパイラの中間コードによって表されるAddIノードとそのオペランドsrc1 / src2に一致します。

レジスタ、オペランド、および命令の説明が完了すると、CPUアーキテクチャに対するJVMのサポートはほぼ完了します。この時点で、前述のCPUアトミック操作とメモリバリアを忘れてはなりません。次のページのPPTに示すように、HotSpotは非常に明確なアトミック操作とメモリバリアインターフェイスを定義しており、プロセッサの特性に応じて1つずつ実装するだけで済みます。誰もがアトミック操作に精通しているので、メモリバリアとは何ですか?次のセクションで詳細な紹介をします。

プロセッサメモリモデルとJVMの実装

プロセッサメモリモデルがJVM設計に与える影響について話し合いましょう。なぜこのトピックだけをリストするのですか?長年の実務経験から、JVMの実装では、エンジニアのレベルがプロセッサメモリモデルとJVMの適応であることがほとんどのテストであることがわかります。作業のこの部分は、仮想マシンがプロセッサ上で安定して実行できるかどうかを決定します。それがみんなの注目を集めることができることを願っています。

プロセッサメモリモデルには、長所と短所があります。強力なメモリモデルはX86で表され、弱いメモリモデルはARMおよびPowerPCアーキテクチャで表されます。では、プロセッサメモリモデルの強度はどのように定義されていますか?次のPPTは、メモリモデルの強度を分割するための基礎を示しています。プロセッサがメモリアクセス命令の並べ替えを許可する量に応じて。一般に、メモリアクセス命令の並べ替えが許可されるほど、プロセッサメモリモデルは弱くなり、その逆も同様です。メモリフェッチ命令は、読み取り(ロード)と書き込み(ストア)の2つの操作に分けられます。したがって、考えられる並べ替えシナリオには、読み取り-読み取り(ロード/ロード)、読み取り-書き込み(ロード/ストア)、書き込み-読み取り(ストア/ロード)、および書き込み-書き込み(ストア/ストア)の並べ替えが含まれます。X86アーキテクチャプロセッサでは、書き込みと読み取り(ストア/ロード)の並べ替えのみが許可されていますが、ARMとPowerPCでは上記の4つの並べ替えが許可されています。したがって、X86は一般に強力なメモリモデルと見なされ、ARMとPowerPCは弱いメモリモデルと見なされます。

ただし、プログラミング中、特に同時プログラミングでは、プロセッサの並べ替え動作を禁止する必要がある場合があります。このとき、完了するにはメモリバリアを使用する必要があります。いわゆる「メモリバリア」とは、プロセッサハードウェアによってサポートされ、特定のメモリアクセス命令の並べ替えを禁止するために特に使用されるマシン命令を指します。次のページのPPTに示すように、HotSpot仮想マシンは、4つの可能な並べ替えシナリオに対応するメモリバリアインターフェイスを提供します。たとえば、X86プロセッサの書き込みと読み取りの並べ替えを禁止する場合は、メモリバリアインターフェイスOrderAccess :: storeload()を呼び出すだけで済みます。上記の4つの基本的なインターフェースに加えて、取得、解放、およびフェンスのインターフェースも仮想マシンで定義されます。その中で、acquireは読み取り/読み取りおよび読み取り/書き込みの並べ替えを禁止でき、releaseは読み取り/書き込みおよび書き込み/書き込みの並べ替えを禁止でき、fenceはすべての並べ替えを禁止できます。

コンパイラは、命令生成段階でプロセッサのメモリモデル特性に完全に適応する必要があります。次のPPTは、C2コンパイラのMemBarStoreStore中間ノード、X86アーキテクチャおよびAarch64アーキテクチャでのターゲットコードの生成を示しています。MemBarStoreStore中間ノードのセマンティクスは、プロセッサが書き込みと書き込みを並べ替えることを禁止することです。X86メモリモデルでは書き込みと書き込みの並べ替えが許可されていないため、中間ノードは、セマンティックの正確性を確保するためにX86アーキテクチャで追加のマシン命令を生成する必要はありません。Aarch64アーキテクチャプロセッサ自体が書き込み/書き込みの並べ替えを許可するため、ノードのセマンティクスを正しく実装するには、追加の書き込み/書き込みメモリバリアが必要です。一般に、弱いメモリモデルアーキテクチャは通常、より多くのメモリバリアを生成する必要があります。

JVMがプロセッサアクセスモデルを適切に適合させない場合はどうなりますか?それは間違いなくバグを引き起こします。このようなバグは通常、ランダム性、発散、および多様な外観の特性を持っているため、分析とデバッグが困難になります。OpenJDKメモリアクセスモデルが正しく適合されていないことを解決したバグ(JDK-8229169)を共有します。このバグは最初にjdk14で修正され、次にjdk8やjdk11などのLTSバージョンにバックポートされました。

このバグは、HotSpotガベージコレクションフレームワークの作業盗用フェーズにあり、シリアルGCを除くすべてのガベージコレクターに影響します。バグのメカニズムは、プロセッサがGenericTaskQueue :: popメソッドを実行すると、_ageの2つの読み取り操作(次のページのPPTに黄色のフォントで示されている)がプロセッサによって順序が狂うことです。解決策は、2つの読み取り操作の間に読み取りメモリバリア(PPTに緑色のフォントで表示)を追加して、プロセッサが順不同で読み取ることを禁止することです。X86プロセッサは順不同の読み取りを許可しないため、X86にこのメモリバリアを追加する必要はありません。PPTの右下隅にある変更方法を使用してみませんか?この質問に対する正解は、OrderAccess :: loadload()を追加してX86も修正する必要があるということです。これは、X86は実行中に読み取り操作を並べ替えませんが、コンパイラがこのコードをコンパイルするときにこのコードを並べ替える場合があるためです。コンパイル中にコードが並べ替えられるのを防ぐために、X86にもこのパッチが必要です。上記の分析から、JVMのOrderAccessメモリアクセスバリアには、プロセッサとコンパイラの並べ替えを禁止する機能もあることがわかります。今後の開発過程では、この点にもっと注意を払ってください。

上記は私が今日あなたと共有したものです。皆さん、ありがとうございました!さらに、誰もが注意を払い、Tencent Kona JDK8にスターを付けることを歓迎します。 

https://github.com/Tencent/TencentKona-8

同時に、すべての優れた開発者は、Tencent JVM R&Dチームに参加するか、以下のQRコードをスキャンするか、[全文を読む]をクリックして参加してください。

舞台裏の返信キーワード[GIAC]は、ゲストにPPTを共有させることができます。

おすすめ

転載: blog.csdn.net/Tencent_TEG/article/details/108301544