GraalVM 21.2 with lots of native image usability improvements

原文はこちら。
The original entry was written by Oleg Šelajev (Developer advocate for GraalVM at Oracle Labs).
https://medium.com/graalvm/graalvm-21-2-ee2cce3b57aa

GraalVM 21.2 が本日リリースされました。この記事では21.2で利用可能な、最も目立つ、重要でエキサイティングなアップデートに焦点を当てます。

まず最初に、GraalVM 21.2はいつもの場所でダウンロードできます。

GraalVMは複数個のコンポーネントから構成されており、毎リリースですべてのコンポーネントに改良が加わっています。もし、特定のコンポーネントに興味があり、変更点の詳細を知りたい場合は、リリースノートをチェックしてください。

GraalVM 21.2 Release Notes
https://www.graalvm.org/release-notes/21_2/

Native Image

ではまず、Native Imageの新機能と注目すべき機能をご紹介します。6月には、JUnit 5テストをサポートするNative Image用の新しいGradleおよびMavenプラグインをリリースしました。これにより、アプリケーションのNative Imageのビルドが容易になり、 Native ImageモードでJUnitテストを実行して、コードがどのように動作するかを確認できるようになりました。

Sample Gradle output when running JUnit tests in native image mode.

最初のリリース以降、様々なバグフィックス、改善、クリーンアップを含む2つのマイナーリリースがありました。もしNative Imageで動作するアプリケーションやライブラリを管理しているなら、この2つのテストを使ってNative Imageで動作確認することを検討してください。

Gradle and Maven Plugins for Native Image with Initial JUnit 5 Testing Support
https://medium.com/graalvm/gradle-and-maven-plugins-for-native-image-with-initial-junit-testing-support-dde00a8caf0b
https://logico-jp.io/2021/06/17/gradle-and-maven-plugins-for-native-image-with-initial-junit-5-testing-support/

もう一つの品質向上は、Native Imageが自動的に不要なセキュリティプロバイダを削除するようになったことです。到達可能なセキュリティプロバイダは静的解析によって検出されます。つまり、--enable-all-security-services のようなオプションは非推奨で、将来的には削除される予定です。-H:-EnableSecurityServicesFeatureを使って自動検出を完全に無効にし、プロバイダを手動で登録することも可能です。詳しくは、以下のドキュメントをご覧ください。

JCA Security Services in Native Image
https://www.graalvm.org/reference-manual/native-image/JCASecurityServices/

21.2で追加された非常に喜ばしい点の一つに、実行時にClassLoader.loadClassの呼び出しをサポートするための、クラスの事前定義の実装があります。実行時にロードが必要なクラスは、ビルド時に静的解析で利用できるようにして、closed world analysisに含まれるようにする必要がありますが、それ以外では、実行時に任意のタイミングでクラスロードするコードパターンが、期待通りにNative Imageで動作するようになりました。

もう一つの興味深いGraalVM 21.2で強化されたインフラストラクチャとして、-H:+AllowVMInspection付きでビルドされたNative Imageが、Javaで書かれたJFRイベントをサポートするようになったことです。

Class Event
https://docs.oracle.com/en/java/javase/11/docs/api/jdk.jfr/jdk/jfr/Event.html
https://docs.oracle.com/javase/jp/11/docs/api/jdk.jfr/jdk/jfr/Event.html

実行時にJFRイベントを記録するには、JFRサポートとJFR記録をコマンドラインオプション(-XX:+FlightRecorder-XX:StartFlightRecording)で有効にする必要があります。実際に実装されているイベントはそれほど多くありませんが、イベントを実装したり、アプリケーションコードからイベントを発するためのアーキテクチャは用意されています。

以下の例で、カスタムイベントが実際にどのように見えるかを確認できます。

import jdk.jfr.Event;
import jdk.jfr.Description;
import jdk.jfr.Label;
public class Example {
@Label("Hello World")
@Description("Helps programmer getting started")
static class HelloWorld extends Event {
@Label("Message")
String message;
}
public static void main(String... args) {
HelloWorld event = new HelloWorld();
event.message = "hello, world!";
event.commit();
}
}
view raw Example.java hosted with ❤ by GitHub

Native Imageにビルドするためには、 -XX:+FlightRecorder -XX:StartFlightRecording=”filename=recording.jfr” を付けて実行します。

例えばVisualVMでは以下のように見えます。

Compiler Updates

アップデートの都度、コンパイラの改良や新たな最適化が追加され、一部のワークロードで利益が得られるようになっています。これらの改善は時間の経過とともに蓄積され、大幅なパフォーマンスの向上につながります。もし、最近アップデートされていないのであれば、これらの改善点を活用する良い機会です。21.2では、いくつかの興味深い最適化機能がコンパイラに追加されています。まずは、Oracle Java SE Subscriptionに含まれるGraalVM Enterpriseで利用可能なものからご紹介します。

カウントされたループに対するループリミット解析を改善しました。これにより、コンパイラはループに先行する制御フローも解析して誘導変数を推論します。これにより、以下の例のようなカウントされないループでも、高度な最適化が可能になります。

long i = 0;
  if (end < 1) return;
  do {
      // body
      i++;
  } while (i != end);

ここでコンパイラは,ループの前に得られた i が 0 で end ≥ 1 であるという情報を組み合わせて end > i であることを証明し,そのデータをループ本体の最適化に使用します。

また、カウントされていないループに対する新しい Strip Mining最適化を追加しました。これはデフォルトで有効です。このしくみを使うと、ループの繰り返しをパーツに分割し、後で最適化しやすくできます。

典型的なStringBuilderパターンを使用するコードのコンパイルを改善しました。JDK11のcompact stringを認識することで、JDK11ベースのGraalVMビルドでこれらのパターンのサポートを強化しました。

続いてGraalVM Community Editionに関しては、コンパイラの特筆すべき改善点として、Speculative Guard Movementという最適化が追加されました。これは、例えば配列の境界チェックなどのループ不変のガードを、ループ内からループ外に移動させようとするものです。これにより、関連するワークロードが劇的に改善されます。

また,long型のカウントループにおけるセーフポイント除去メカニズムを改善しました。コンパイラは、誘導変数の範囲がInteger.MAX_VALUEからInteger.MIN_VALUEであることを静的に証明できる場合、long型の誘導変数を持つループの安全点を排除できるようになりました。以下のforループの例を考えてみましょう。

for (long i = Integer.MIN_VALUE; I < Integer.MAX_VALUE; I++) { 
  // body 
}

コンパイラは、iがlong型であっても整数の範囲内でしか反復しないことを静的に証明できるようになりました。その結果、整数オーバーフローを考慮する必要がなくなり、ループが最適化されます。

さらに、今回のリリースでは、デフォルトでは有効になっていない、実験的な最適化がいくつかあります。実験的な最適化の1つであるWrite Sinkingは、書き込みをループの外に移動させようとするものです。この機能は -Dgraal.OptWriteMotion=true で有効にできます。GraalVM Entepriseでは利用できるものの、まだデフォルトでは有効になっていないもう一つの最適化は、シーケンシャルコードのための新しいSIMD(Single Instruction Multiple Data)ベクトル化最適化です。-Dgraal.VectorizeSIMD=true でこの最適化を試すことができます。まだ試していない方は、次回の単独記事で、この機能の詳細と、コードにどのようなメリットがあるかをご紹介しますので、ご期待ください。

Polyglot Runtime & Truffle framework

Truffle には、新しいcompilation queuing heuristicが導入され、デフォルトで有効になっています。この新しいヒューリスティックは、多くのワークロードでpolyglotランタイムのウォームアップ時間を改善します。

簡単にどういうものか紹介しましょう。例えば、次のようなJavaScriptの合成コードがあなたのアプリケーションだとします。このアプリケーションは2つの関数をループで実行しており、どちらもJITコンパイルのしきい値に達しているため、コンパイルする必要があります。

function lowUsage() {
for (i = 0; i < COMPILATION_THRESHOLD; i++) {
// Do something
}
}
function highUsage() {
for (i = 0; i < 100 * COMPILATION_THRESHOLD; i++) {
// Do something
}
}
while(true) {
lowUsage();
highUsage();
}
view raw pseudocode.java hosted with ❤ by GitHub

GraalVMの以前のバージョンでは、コンパイルの優先順位を決めるアルゴリズムは先着順でした。この場合、あまり使われない関数(lowUsage)が先にコンパイルされるため、よく使われる関数(highUsage)が先にコンパイルされず、あまりよいコンパイル順序ではありません。

GraalVM 21.2では、このポリシーは単にhotnessだけではなく、multi-tier(多層)の設定を考慮し、第1層をより速くコンパイルすること、コンパイルの履歴や最適化解除により以前から重要と判断されていたコードを優先すること、そしてコードのhotnessと同時にアクティブに使用されていることを加重した組み合わせを考慮します。これにより、すべてのGraalVM言語のウォームアップが改善されます。

このヒューリスティック機能を微調整したり、完全に無効にするための追加の設定オプションがあります。詳しくは、ドキュメントをご覧ください。

Truffle Approach to the Compilation Queue
https://github.com/oracle/graal/blob/master/truffle/docs/TraversingCompilationQueue.md

もう一つの変更点は、polyglot contextを埋め込む際の特定のAPIコールで、JVMCI(コンパイラーをJava HotSpot VMに差し込むために使うコンパイラー・インターフェース)の新バージョンを必要とすることです。全てのJDKバージョン用のGraalVMディストリビューションには、アップデート済みのJVMCIが含まれていますが、異なるJDKでGraalVM言語を使用しており、モジュールパス上でGraalVMコンパイラを有効にしている場合は、完全な互換性のためにJDK-8264016を含む以下のJDKバージョンのいずれかを使用するようにしてください。

  • Other JDK 11: Oracle JDK 11.0.13 (2021-10-19)で対応。OpenJDKは未定
  • Other JDK 16: JVMCIのアップデート予定はない
  • Other JDK 17: JVMCIの新バージョンは既に早期アクセスビルドに統合済み

[JVMCI] add some thread local fields for use by JVMCI
https://bugs.openjdk.java.net/browse/JDK-8264016

JDK-8264016を含まないJDKを使うと、強制的なコンテキストキャンセル(Context.close(true))や割り込み(Context.interrupt(Duration))を使用すればエラーが発生します。最高の体験を得るためには、アプリケーションの実行にGraalVMディストリビューションの使用を検討するか、これらのAPIの使用を避けるようにしてください。その他の回避策については、リリースノートに記載されています。

Polyglot Embedding
https://www.graalvm.org/release-notes/21_2/#polyglot-embedding

また、Truffle関連の変更点ということで、GraalVM 21.2では、GraalVM上で言語やツールを実装するプロジェクトのために、いくつかのエキサイティングなアップデートがあります。Truffleライブラリは、事前実行せずにAOTコンパイルの準備ができるようになりました。詳細は、ExportLibrary.useForAOTのドキュメントとAOTチュートリアルをご覧ください。

ExportLibrary.useForAOT
https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/library/ExportLibrary.html#useForAOT–
AOT Tutorial
https://github.com/oracle/graal/blob/master/truffle/docs/AOT.md

JavaScript

JavaScript の実装では、New Set Methods、実験的な演算子オーバーロードのサポート、あるいは RegExp Match Indices のような最新の提案に対する実装の追加と更新が続けられています。これらを有効にする方法の詳細は、リリースノートに記載されています。

New Set methods
https://github.com/tc39/proposal-set-methods
Operator Overloading
https://github.com/oracle/graaljs/blob/master/docs/user/OperatorOverloading.md
RegExp Match Indices for ECMAScript
https://github.com/tc39/proposal-regexp-match-indices
Release Notes
https://www.graalvm.org/release-notes/21_2/#javascript

それに加えて、Graal.jsでは、polyglot Contextでunhandled promise rejections(未処理のpromiseの拒否)を追跡するオプションが追加され、開発エクスペリエンスが強化されました。デフォルトでは、オプションはnoneに設定されており、未処理のpromiseの拒否を追跡しませんが、js.unhandled-rejectionsオプションを使って設定し、開発を楽にできます。

Ruby

いつものように、Ruby では互換性とパフォーマンスの向上が継続的に行われています。その中でも、非常にインパクトのある機能として、Rubyのメソッドや定数を、名前ごと、クラスごとに想定して、正確に無効化する機能が挙げられます。Ruby のコードをロードする際には、Ruby のセマンティクスによりメソッドが次々と追加され、ファイルロード時に多くのコードが実行されます。クラスごとの仮定(他のRuby実装や今回の変更前のTruffleRubyでの動作)では、そのクラスのメソッドを呼び出すすべてのコールサイトが無効になります。正確な無効化では、次の呼び出しのために実際に別のメソッドを呼び出す必要があるコールサイトのみを無効にします。これにより、実際のコードでのウォームアップが改善されました。

これらの変更に伴い、Ruby 2.7.3に更新しましたが、resolvstdlibは更新しておりません(2.7.3のresolvにはバグがあります)。また、新しいTruffleRuby::ConcurrentMapデータ構造を追加し、concurrent-rubyで使用できるようにしました。

Ruby 3.0 takes a long time to resolv DNS of nonexistent domains
https://bugs.ruby-lang.org/issues/17748
Concurrent Ruby
https://github.com/ruby-concurrency/concurrent-ruby

その他の多くの変更点や修正点については、リリースノートをご覧ください。

TruffleRuby – GraalVM Community Edition 21.2.0
https://github.com/oracle/truffleruby/releases/tag/vm-21.2.0

Python

このリリースでは、_pickleをpure Python版よりも高速なバージョンとして実装し、シリアライズを高速化しました。

他の言語との相互運用性を高めるために、Truffleのハッシュ実装を使用しているdict型が期待通りに動作するように改善しました。

いつものように、特にウォームアップ時や共有エンジン構成でのパフォーマンスの改善に多くの作業を費やしました。

また、多くの互換性の改善も行われています。詳細はリリースノートをご覧ください。

Release Notes
https://www.graalvm.org/release-notes/21_2/#python

LLVM bitcode runtime

21.2では、C++関連の改善がいくつか行われています。macOS 11.3のC++でLLVMツールチェーンが正しく動作しない問題を修正しました。また、言語間の相互運用性による、C++でのvirtual callのサポートを追加しました。

GraalVM Enterpriseのマネージドモードも改善されました。musllibctoをバージョン1.2.2にアップデートし、マネージドモードでのpthreadとpthread同期プリミティブのサポートを追加することで、既存のネイティブコードベースとの互換性を改善しました。

Limitations and Differences to Managed Execution on Top of GraalVM Enterprise
https://www.graalvm.org/reference-manual/llvm/NativeExecution/#limitations-and-differences-to-managed-execution-on-top-of-graalvm-enterprise

FastR

FastR は引き続き互換性の向上に取り組んでいます。2021-02-01 CRAN スナップショットのパッケージのサポートを改善しました。

  • testthat 3.0.1 は部分的にサポートされています。
  • tibble 3.0.6 , vctrs 0.3.6, data.table 1.13.6 はほぼサポートされています。
  • dplyr 1.0.3, ggplot 3.3.3, knitr 1.31 のサポートは現在作業中です。

特定のパッケージのサポートに興味があれば、ぜひご連絡ください。

WebAssembly

WebAssembly ランタイムでは、WebAssembly を使用する多くの NPM モジュールのテストに合格するために、多くの互換性の改善といくつかのバグ修正が行われました。

Java on Truffle

GraalVM 21.2におけるJava on Truffleの重要な点の1つは、新しいHotswap Plugin APIです。これにより、実行中のアプリケーションを再起動せずにコードを再読み込みできます。このAPIは、フレームワークの開発者が、IDEでのソースコードの編集に応じてアプリケーションへの変更の反映をより適切にコントロールできるようにすることを目的としています。これは、適切なフックを設定することで実現しています。ここで、指定したHotSwapイベントで発生する様々なHotSwapリスナーを登録できることを主たる設計原則としています。例えば、特定のサービスプロバイダの実装が変更された場合における静的イニシャライザの再実行、一般的なHotSwap後のコールバックの実行やフックなどがあります。

最も驚くべきことは、APIが通常のJavaコールで構成されている点です。これにより、バイトコード操作を操作するソリューションよりもはるかに統合とメンテナンスが容易になっています。

詳細は、plugin APIについて記述しているドキュメントをご覧ください。

Truffle on Java HotSwap Plugin API
https://www.graalvm.org/reference-manual/java-on-truffle/hotswap-plugin/

21.2では、バイトコードのディスパッチが改善され、Java on Truffleで使用されるインタプリタが約15~30%高速化されています。インタプリタの速度は、アプリケーションのウォームアップの速さに非常に重要な要素です。

また、21.2では、MapMap.EntryListIteratorIterableを実装したJava on Truffleオブジェクトの、Javaホストコードを含む他の言語との相互運用性が改善されました。

Tools

VisualVMは、JDK 17のサポートや、コマンドラインやその他の外部ツールから機能を制御できるようにするなど、数多くの優れた改良が施されています。後者は、VisualVMとGraalVM extensionがインストールされたVSCodeのスムーズな統合を可能にします。IDEからすぐにアプリケーションのプロファイリングを行うようにVisualVMに指示できます。

Note the VisualVM tab in the lower left corner with actions available directly from VS Code.

VisualVMは、生存しているJavaプロセスからJFR記録を保存することもできますし、Profilerタブには、新しいLock Contentionビューがあります。日常的なプロファイリングにVisualVMを使用している方にとっては、これまで以上に強力なツールとなるでしょう。

Documentation

最後になりましたが、ウェブサイトに掲載されるGraalVMのドキュメントを再構成しました。ドキュメントは、GitHubのメインプロジェクトリポジトリで公開されています。

これにより、GraalVMへの貢献がこれまで以上に容易になり、エコシステムの中で他の多くの開発者の生活を向上させることができます。もし、ドキュメントの内容が不明瞭であったり、お気に入りの機能やチューニングオプションの説明が不完全であったりする場合は、あなたの専門知識を共有し、プルリクエストでプロジェクトを支援することをご検討ください。疑問があれば、貢献方法に関する簡単なガイドをごらんください。

How to Contribute to GraalVM Documentation
https://github.com/oracle/graal/tree/master/docs

なお、GraalVM Enterpriseのドキュメントは以下のURLからどうぞ。

Oracle GraalVM Enterprise Edition
https://docs.oracle.com/en/graalvm/index.html

毎回のリリースと同様に、GraalVMコミュニティの皆様に対し、あらゆるコラボレーション、フィードバック、ディスカッション、そしてGraalVMの使用方法に関するストーリーを共有していただいたことに感謝します。

冒頭に申し上げたとおり、ダウンロードはいつものようにウェブサイトから可能です。

ご意見、ご感想など、ご遠慮なくお寄せください。チームへの連絡手段はいくつかありますが、お好きなものをお選びください。Slack、GitHub、Twitter、またはメールにてご連絡ください。

Slack (Invitation)
https://graalvm.org/slack-invitation
GitHub
https://github.com/oracle/graal
Twitter
https://twitter.com/graalvm
Mailing list
https://oss.oracle.com/mailman/listinfo/graalvm-users

コメントを残す

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

WordPress.com ロゴ

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

Google フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中