State of Valhalla – Section 1: The Road to Valhalla

原文はこちら。
The original article was written by Brian Goetz (Java Architect, Oracle)
http://cr.openjdk.java.net/~briangoetz/valhalla/sov/01-background.html

Section 1: The Road to Valhalla英語日本語
Section 2: Language Model英語日本語
Section 3: JVM Model英語日本語
Section 4: Translation英語日本語

Project Valhallaの目標は、より柔軟なフラットなデータ型をJVMベースの言語にもたらし、プログラミングモデルを最新のハードウェアのパフォーマンス特性に合わせることです。中核となる機能はインライン型ですが、JVMの型システムのこのような基本部分の揺れは、ジェネリックスの特殊化のような多くの機能と課題だけではなく、既存のAPIをインライン型や特殊化されたジェネリクスへの互換性ある移行を可能にするツールをももたらします。

Project Valhalla
http://openjdk.java.net/projects/valhalla

Project Valhallaは2014年にスタートしました(当時、James Goslingが “six Ph.D theses, knotted together” と説明していました)。過去5年間で、5個のプロトタイプを作成してきました。それぞれは別々の問題の側面を理解するためのものです。現在我々は以下の状態にあると考えています。

  • Java言語と仮想マシンを値型で強化するための明確で一貫したパスがある
  • 既存のジェネリックと完全に相互運用し、既存の値ベースのクラスをinlineクラスに、既存のジェネリクスクラスから特殊化されたジェネリクスに移行する互換性のあるパスがある

この一連のドキュメントは、そのパスをまとめたものです(開始地点と落ち着き先を比較したい場合は、設立文書を参照してください)。

State of the Values
http://cr.openjdk.java.net/~jrose/values/values-0.html

Motivation

JVMの型システムには、8つのプリミティブ型(int、longなど)、クラス(Identityを持つ異質な集合体)、および配列(Identityを持つ同質な集合体)が含まれます。これらのビルディングブロックは柔軟で、必要なデータ構造をモデル化できます。利用可能なプリミティブ型にうまく当てはまらないデータ(例えば複素数、3次元ポインタ、タプル、10進数値、文字列など)は、オブジェクトを使って簡単にモデリングできます。ただし、オブジェクト(VMが十分に狭いスコープで別名が付けられていないことを証明できない場合)はヒープに割り当てられ、オブジェクトヘッダー(通常は2マシンワード)が必要であり、メモリの間接参照を介して参照する必要があります。例えば、XYポイントオブジェクトの配列は、以下のメモリレイアウトを有しています。

Layout of XY points
Layout of XY points

Java仮想マシンが設計されていた1990年代初頭、メモリフェッチのコストは、加算などの計算操作と同程度でした。複数階層のメモリキャッシュと今日のCPUの命令レベルの並列処理により、1回のキャッシュミスが最大1000算術(命令)発行スロットに相当する可能性があり、相対コストが大幅に増加します。その結果、JVMが好むポインターが豊富な表現は、小さなデータアイランド間の多くの間接参照を伴うため、現在のハードウェアにとって理想的ではなくなりました。開発者が今日のハードウェアのパフォーマンスモデルとデータレイアウトを一致させることができるようにすることを目指し、Java開発者に対して、抽象化や型安全性を損なうことなく、フラット(キャッシュ効率)および高密度(メモリ効率)のデータレイアウトへの簡単なパスを提供します。

Object identity

この不幸なレイアウトの根本的な原因はオブジェクトのアイデンティティです。現在、すべてのオブジェクトインスタンスにはオブジェクトIDがあります(90年代前半、「すべてがオブジェクトである」は魅力的なマントラであり、それを実現するためのパフォーマンスコストは面倒ではありませんでした)。アイデンティティによって可変性がもたらされます。オブジェクトのフィールドを変更するには、どのオブジェクトを変更しようとしているか分かる必要があります。アイデンティティは多態性(polymorphism)もサポートしています。各オブジェクトインスタンスはクラスに関連付けられており、クラスからメモリレイアウトと仮想メソッドディスパッチを導出できます。ただし、(多くの場合)可変性と多態性を回避するクラスであっても、オブジェクトの等価性(==)、同期、System::identityHashCode、弱参照などを含むさまざまなアイデンティティ依存の操作によってアイデンティティを観察できます。その結果、VMは、ユーザーが最終的にアイデンティティ依存の操作(たとえその操作が結局なされないとしても)を実行する場合に備えて、IDを悲観的に保持する必要があります。そのため、現在のポインタをたくさん使うメモリレイアウトになります。

Inline classes

Inlineクラスは、アイデンティティを明示的に否認する異種集合です。そのため、Inlineクラスは不変でなければならず、レイアウト多態性であってはなりません。しかし、これら(Mutability、layout-polymorphic)を放棄する意思がある場合は、よりフラットで高密度のメモリレイアウトだけでなく、最適化された呼び出し規約(inlineクラスインスタンスは、ヒープではなくスタックまたはレジスタで渡すことができます)を見返りに受けることができます。次のように宣言するだけで、クラスがinlineクラスであることを示すことができます。

inline class Point {
    int x;
    int y;
}

inlineクラスには、通常の(アイデンティティ)クラスと比較していくつかの制限があります。

  • final
  • fieldはfinal
  • 継承に参加できない

これらの制限を受け入れる見返りに、ランタイムはそれらをデータのみとして自由に扱うことができるため、ヒープオブジェクトへの参照としてではなく、値によって配列またはオブジェクトにフラット化して渡すことができます。この命令により、ランタイムは次のようなメモリレイアウトを日常的に提供できます。

Flattened layout of XY points
Flattened layout of XY points

これは、以前のバージョンよりも間接参照がなくてフラットであり、ヘッダーがなく高密度です。これらの制限を除き、inlineクラスは、クラスで利用できるメソッド、コンストラクタ、フィールド、カプセル化、型変数、注釈などのほとんどのメカニズムを使用できます。スローガンは ”Codes like a class, works like an int”「クラスのようなコードがintのように機能する」です。インライン型は、「より高速なクラス」または「ユーザー定義可能なプリミティブ」と同等に考えることができます。

すべてのレベルでインライン型のアプリケーションがあります。数値、日付、カーソル、Optionalのようなラッパーなど、多くのAPI抽象化は、当然inline型です。 HashMapなどの多くのデータ構造では、実装でInline型を使用して効率を改善できますし、言語コンパイラは、組み込みの数値型、タプル、複数のreturnのような機能のコンパイルターゲットとしてインライン型を利用できます。

Generic specialization

Javaジェネリクスの初期の妥協点の1つは、ジェネリック型の変数は、プリミティブ型ではなく参照型でのみインスタンス化できる、という点です。ボクシングを使わなければプリミティブ型を生成できず、常にいらいらさせられてきました。それは抽象化や再利用の利点を得るためにパフォーマンスを犠牲にせざるを得ないためです。インライン型を追加すると、この制限はさらに厳しくなります。上記のPointのようなフラット化可能なデータ型を作成することができれば、ArrayList<Point>の背後にフラット化されたPointの配列がないのは、ポイントを無効にしているように思われます。

パラメトリックな多態性は、常にコードのフットプリント、抽象化、および特異性の間のトレードオフを伴いますし、異なる言語では異なるトレードオフを選択してきました。

一方で、C ++はテンプレートのインスタンス化ごとに特殊化されたクラスを作成し、異なる特殊化は互いに型定義の関係を持ちません。このような異種間の変換は、a+bのような式がaとbのインスタンス化された型の+の動作と相対的に解釈できる点で高い特異性を提供しますが、大きなコードフットプリントだけでなく、抽象化の損失を伴います。JavaにはFoo<?>に相当する型はありません。

他方、すべての参照インスタンス化に対して1つのクラスを生成し、プリミティブなインスタンス化をサポートしない現在の型消去された実装がJavaにはあります。すべての参照インスタンス化に対して1つの型とオブジェクトレイアウトをインスタンス化できるため、このような同種の変換では高度な再利用が可能になります。プリミティブではなく参照型のみを範囲とすることができる制限は、参照型とプリミティブ型の操作に異なるバイトコードを使用するJVMのバイトコードセットにそのルーツがあります。

ほとんどの開発者は消去をある程度嫌がるものですが、このアプローチには他の方法では得られなかった強力な利点があります。それは、段階的な移行の互換性です。これは、既存のソースやバイナリクラスファイルを壊すことなく、非ジェネリックスからジェネリックスへとクラスを互換的に進化させる能力であり、クライアントやサブクラスには、すぐもしくは後に移行する、あるいは全く移行しないという柔軟性を残しています。ユーザーにジェネリックスを提供しつつも、すべてのライブラリを捨ててしまうという代償は、Javaがすでに大規模で活気に満ちたインストールベースを持っていた2004年には、悪い取引だったでしょう(今日ではもっと悪い取引になるでしょう)。

私たちの今日の目標は、2004年よりもさらに野心的なものです。それは、ジェネリクスを拡張してプリミティブやInlineクラスの上にインスタンスを作成できるようにし、特殊化された(ヘテロな)レイアウトで、段階的な移行機能を維持しながら、ジェネリックを拡張することです。(さらに、抽象的な移行互換性だけでは十分ではありません。コレクションやストリームを含むライブラリを実際に移行したいのです)。

A brief history of Project Valhalla

Project Valhallaは野心的な目標を掲げており、その範囲は深く広いもので、クラスファイル形式、VM、言語、ライブラリに影響を与えます。過去6年間、私たちはいくつかのプロトタイプを作成してきましたが、それぞれのプロトタイプはこの問題のさまざまな側面の理解を深めることを目的としていました。

最初の3つのプロトタイプは、プリミティブに特化したジェネリックの課題を探るもので、バイトコードの書き換えによって動作していました。最初のプロトタイプ(Model 1)は、主に特殊化の機構に狙いを定めており、コンパイラが保持してspecializerが対応する必要がある型の振る舞いを特定していました。2番目のプロトタイプ(Model 2)では、特殊化可能なジェネリクスモデルでワイルドカード(ひいてはブリッジメソッド)をどのように表現するかを探り、既存のライブラリを移行する際の課題を検討しはじめました。3番目のプロトタイプ(Model 3)では、これまでに学んだことを統合し、特殊化されたジェネリックスを表現するために使用できる実用的なクラスファイル形式を構築しました。(これらのそれぞれの実験から得られた課題と教訓の簡単なツアーについては、以下の講演の動画をご覧ください(スライドはこちら)。

Brian Goetz on Generic Specialization

これらの実験の結果は玉石混交でした。うまくいったものは、特殊化可能なジェネリックスクラスを書き、わずかにハッキングされただけのJVM上で実行できました。一方では、障害物がたくさんありました。既存のクラスは移行を困難にするような仮定に満ちており、私たちがまだ良い答えを持っていない多くの問題がありました。

そこで、Minimal Value Types(実用最小限の型)のプロトタイプを使って、別の方向から問題を攻めました。このプロトタイプの目標は、VM でフラットで高密度なレイアウトを実装できることを証明することでした。

Valhalla の転機となったのは、「L World」と呼ばれる最新のプロトタイプでした(Inlineクラスがオブジェクト参照と L キャリアを共有できるからです)。初期の段階では、Inlineクラスがプリミティブのようなもので、型記述子、バイトコード、トップ型が分離されているVMモデルを想定していました。というのも、参照型とInline型を一つの型記述子、バイトコード、型のセットで統一するのは大変なことだと思われたからです。

L-worldではこの統一を試みて、(いささか驚いたことに)大きな妥協をせずに実現しました。L-world が提供する型と記述子の統一は、プロトタイプの初期段階で遭遇した多くの課題を解決してくれました。

Moving Forward

Project Valhallaを大きく分けて2つのフェーズに分けて提供する予定です。一つはInlineクラス、そしてもう一つは特殊化されたジェネリクスです。最初のフェーズでは、Java 言語と仮想マシンでのInlineクラスのサポートに焦点を当てます。Part 2から4でこのフェーズについて説明しています。このフェーズでは、JDKでのInlineクラスの使用のための基礎を築き、既存の値ベースのクラス(Optional や LocalDateTime など)をInlineクラスに移行することも予定しています。

第二フェーズではジェネリックスに注力し、ジェネリック型システムを拡張して(プリミティブを含む)Inlineクラスでのインスタンス作成をサポートし、特殊化されたレイアウトをサポートするようJVMを拡張します。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中