WebAssemblyで、JITコンパイラに迫る高速なJavaScriptエンジンを実装へ。Bytecode Allianceが技術解説。JavaScript以外の言語でも
「Bytecode Alliance」は、WebAssemblyをWebブラウザだけでなく、デスクトップPCやサーバ、IoTデバイスなどあらゆる環境で、セキュアに実行することを目指している団体です。
Fastly、Mozilla、Arm、Google、マイクロソフト、インテルをはじめとする企業や団体が名前を連ねています。
参考:WebAssemblyをあらゆるプラットフォームでセキュアに実行できるようにする「Bytecode Alliance」発足。インテル、Mozilla、Red Hatなど
同団体は「WASI」と呼ばれる、どのOSやホストシステムでWebAssemblyモジュールが実行されたとしても、安全かつ透過的に共通のAPIでOSやホストシステムの機能を呼び出すための仕様などを策定中です。
例えば、この仕様を用いることでさまざまなプログラミング言語のポータブルなランタイムをWebAssemblyで実装できます。
ただしWebAssemblyで実装できるのはインタプリタとしてのランタイムであり、ネイティブに実装されたJITコンパイラのような、ネイティブコードを動的に生成して実行するといったことは、言語の仕様上できません。
では、WebAssemblyによるランタイムは、JITコンパイラを採用したランタイムよりもつねに遅い実行速度しか期待できないのかと言うと、そうではないと、Bytecode Allianceのブログに投稿された記事「Making JavaScript run fast on WebAssembly」で解説されています。
もしもWebAssemblyでJITコンパイラに迫る実行速度のJavaScriptエンジンが実装できれば、瞬時に起動するサーバレスコンピューティングのJavaScriptエンジンや、JITコンパイラが許されていないiOS上での高速なJavaScript実行が可能になります。
どのような技術を用いるとWebAssemblyによるJavaScriptエンジンがJITコンパイラに迫る性能を実現できるのでしょうか。Bytecode Allianceの記事「Making JavaScript run fast on WebAssembly」(以下、記事)を引用しつつ、ポイントを見ていこうと思います。
下記は、その記事の著者であるLin Clark氏のツイートです。
After years of perf optimizations, JS is running way faster in the browser
— Lin Clark (@linclark) June 2, 2021
Today, we're starting work to optimize JS perf in places where different rules apply, like Serverless and iOS devices
And this is possible because of WebAssembly https://t.co/gajCsded4Z
事前初期化ツールで初期化が13倍も高速に
記事では、JavaScriptエンジンの実行性能に大きくかかわるのは2つの要素「Initialization phaze」(起動時)と、「Runtime phase」(実行時)のスループットだと説明しています。
まず起動時の高速化には、事前初期化ツール(Pre-initializer)の「Wizer」を用いて、ビルド時にJavaScriptエンジンによるJavaScriptコードの初期化処理を実施。その内容は以下の様に説明されています。
At this point, the JS engine has parsed all of the JS and turned it into bytecode, which the JS engine module stores in the linear memory. The engine also does a lot of memory allocation and initialization in this phase.
この時点で、JavaScriptエンジンはすべてのJavaScriptコードを解析し、バイトコードに変換。JavaScriptエンジンモジュールはこれをリニアメモリに格納します。この段階ではまた、JavaScriptエンジンは多くのメモリの割り当てと初期化を行います。
この初期化済みのバイトコードをJavaScriptエンジンに持たせることで、初期化を高速化するとの説明ですが、近い将来、WebAssemblyのモジュールリンクが実現すれば、この初期化済みのバイトコードはJavaScriptエンジンと分離できるようになると。
Currently, we attach this data section to the same module as the JS engine. But in the future, once module linking is in place, we’ll be able to ship the data section as a separate module, allowing the JS engine module to be reused by many different JS applications.
現在のところ、このデータセクションはJavaScriptエンジンと同じモジュールにアタッチされています。しかし将来的にモジュールのリンクが実現すれば、データセクションを別のモジュールとして用意できるようになり、JavaScriotエンジンモジュールは、さまざまなJavaScriptアプリケーションで再利用できるようになるでしょう。
こうした処理によって初期化は圧倒的に高速化されると説明されています。
2KB程度のインラインキャッシングで95%のコードをカバー
続いて、コードの実行時における速度について。
記事では、JITコンパイラによる主要な高速化テクニックとしてインラインキャッシングが挙げられています。
One optimization technique that JITs use is inline caching. With inline caching, the JIT creates a linked list of stubs containing fast machine code paths for all the ways a bit of JS bytecode has been run in the past.
JITコンパイラが使用する最適化技術の一つにインラインキャッシングがあります。インラインキャッシングでは、それまでに実行されたJavaScriptバイトコードすべてについて、JITコンパイラが高速な機械語のパスを含むスタブのリンクリストを作成します。
そして記事によると、このインラインキャッシングのパターンの95%程度が、あらかじめ用意した一般化された2キロバイト程度のインラインキャッシングでカバーできるとのこと。
We discovered that with just a few kilobytes of IC stubs, we can cover the vast majority of all JS code. For example, with 2 KB of IC stubs, we can cover 95% of JS in the Google Octane benchmark. And from preliminary tests, this percentage seems to hold for general web browsing as well.
私たちは、わずか数キロバイトのインラインキャッシュのスタブによって、あらゆるJavaScriptコードの大部分をカバーできることを発見したのです。例えば、Google Octaneベンチマークで用いられているJavaScriptコードの95%を、2KBのインラインキャッシュスタブでカバーすることができました。また、おおまかにテストした範囲でも、一般的なWebブラウジングにおいて同様の割合でした。
こうした高速化テクニックを用いることで、WebAssemblyによるJavaScriptエンジンの実装は初期のJITコンパイラによるJavaSriptエンジンに迫る性能を獲得できつとのこと。
さらにJITコンパイラが行っているようなコードのプロファイリングを導入できれば、現在のJITコンパイラ並みの性能を、より高速な起動時間とともに得られるとしています。
If we can figure out a way to put good tools in place for profiling, though, then there’s a chance we could actually make the JS run almost as fast as today’s JITs (and without the warm-up time!)
もしもプロファイリングのための優れたツールを導入する方法を見つけ出すことができれば、現在のJITコンパイラとほぼ同等の速度でJavaScriptを動作させることができる可能性があります(しかもウォームアップの時間が不要に!)。
MozillaのSpiderMonkeyで実装中
Bytecode Allianceでは、この実装をWebAssemblyランタイムの「Wasmtime」とMozillaのJavaScriptエンジンである「SpiderMonkey」、そして事前初期化ツールの「Wizer」を組み合わせて進めているとのこと。
そして同様の高速化テクニックがJavaScript以外の動的型付け言語でも利用できるだろうと、次のように呼びかけています。
If you’re part of a language community for a language like Python, Ruby, Lua, or others, you can build a version of this for your language, too.
あわせて読みたい
Stack Overflowが投資会社に買収された理由は、グローバル展開を加速し、さらに成長するため
≪前の記事
Kubernetes環境をWindows 10/macOSへ簡単にインストール。「Rancher Desktop」がオープンソースで公開