kakts-log

programming について調べたことを整理していきます

v8 "Launching ignition and Turbofan" 和訳

先日リリースされたv8 5.9において、新たなコード実行パイプラインが導入され、大きくパフォーマンスが向上したとのことです。
今後リリースされるNode.js ver8においてもv8 5.9が使われるため、概要を整理するためにv8本家のブログを和訳してまとめてみました。
v8project.blogspot.jp

Launching ignition and Turbofan

v8 5.9から新しいjavascript実行パイプラインが導入されました。 Chromeでは、バージョン59からこれが導入されます。
新しい実行パイプラインでは、従来と比べてパフォーマンスが大きく向上し、javascript アプリケーションにおいてメモリの使用量に関してもかなり改善されます。

新しいパイプラインは、Ignitionというv8のインタプリタによって動作し、さらにTurboFanとよばれるv8の新しいコンパイラによって最適化されます。

v8 5.9において初めて、IgnitionとTurboFanが導入されました。
これは、2010年からv8によって使われていたFull-codegenとCrankshaftが最新のjavascriptの機能と最適化にキャッチアップできなくなってきたことによります。
v8チームは、Full-codegenとCrankShaftを近々完全に除外する予定で、これはv8が今後シンプルでさらにメンテナブルなアーキテクチャを採用することを意味します。

A Long Journey

IgnitionとTurboFanを組み合わせたパイプラインは3年半ほど開発が続いていました。
この開発により、v8チームは実際に稼働しているjavascriptのパフォーマンス計測の知見を集め、Full-codegenとCrankshaftによるパイプラインの欠点を注意深く考慮しました。
これによりv8チームはjavascript全体の最適化を続けるための基礎を固めることができました。

TurboFanについて

TurboFan プロジェクトは、Crankshaftによる欠点に対処するために2013年後半にスタートしました。
Crankshaftはjavascriptのサブセットのみ最適化することができます。
例えば、Crankshaftは、try-catch-finallyキーワードを利用した例外ハンドリングを行うjavascriptコードを最適化するようには設計されていませんでした。
Crankshaftにおいては、javascriptの新たな機能に対応することが難しく、これらの新機能はほぼ常に9つものプラットフォームのためにアーキテクチャに依存したコードを書く必要がありました。
さらに、Crankshaftのアーキテクチャは、最適化されたマシンコードを生成することのみに制限されています。
これにより、チップアーキテクチャ毎に1万行を超えるコードをメンテナンスするv8チームの要求にも関わらず、パフォーマンスを最大限に発揮できませんでした。

TurboFanはES5当時のすべてのjavascriptの機能に対して最適化するだけでなく、ES2015やそれ以降の新たな機能に対しても最適化を行えるように設計されました。
TurboFanは高レベル・低レベルの間をうまく分割することができるようなレイヤー構造のコンパイラデザインを導入し、アーキテクチャ依存のコードを変更することなく新たな機能を追加することが容易です。
さらに、暗黙的な命令コンパイルのフェーズを導入し、各プラットフォームに対するアーキテクチャ依存のコードを極力減らすことが可能です。
この新たなフェーズの導入により、アーキテクチャ依存のコードは1度きりだけ書いて、後に変更する必要がほとんどありません。
このフェーズや他の機能に対する決定によって、v8がサポートするすべてのアーキテクチャにおいて、メンテナンス性が向上し、コンパイルの最適化コードにかかるコストが軽減しました。

Ignitionについて

v8のIgnitionインタプリタにおける開発当初の動機としては、モバイル端末におけるメモリ消費の削減が挙げられます。
Ignition導入前は、v8のFull-codegenによって生成されたコードは、Chromejavascriptヒープメモリの1/3ものメモリを占めていました。
これにより、実際のwebアプリケーションのデータのための容量が小さくなってしまっていました。

Chrome M53でIgnitionが導入された当時、制限されたRAM容量をもつAndroidバイスにおいて、最適化されていないjavascriptコードのためのメモリ領域が減少していることが ARM64ベースのモバイル端末において確認されました。

後に、v8チームはIgnitionのバイトコードが、Crankshaftがソースコードからリコンパイルするよりも、最適化されたマシンコードを生み出すために活用できるという利点を活かしました。
Ignitionのバイトコードはよりクリーンで、よりエラーを起こしにくいベースライン実行モデルをv8にもたらし、v8のAdaptive Optimization*1の重要な特徴である、Deoptimizationメカニズムをシンプルな構造にしました。
最終的に、バイトコードの生成がFull-codegenコンパイルコードの生成よりも早くなり、Ignitionの実行によってスクリプトの高速化をできるようになりました。

IgnitionとTurboFanのデザインを密接に組み合わせることで、アーキテクチャ全体の利点があります。
例えば、v8チームは、Ignitionのハイパフォーマンスなバイトコードハンドラを手で書いて組み合わせるよりも、
TurboFanの中間表現をバイトコードハンドラの機能を表現するために用い、TurboFanに最適化と多くのプラットフォームのためのコンパイル済みコードを生成させる方針をとりました。
これにより、Igtionプラットフォームがすべてのチップアーキテクチャにおいて改善され、メンテナンスの負荷がなくなりました。

Running the Numbers

ここでは新しいパイプラインによるパフォーマンスとメモリ消費を見ていきましょう。

v8チームは定期的にTelemetry-Catapultフレームワークを使って、javascriptのアプリのパフォーマンスを調査してきました。
以前にブログ記事*2に書いたように、実際のアプリケーションにおけるパフォーマンスのテストを行うことが、いかに我々の最適化の仕事をすすめるかといったことを議論してきました。
Ignition-TurboFanパイプラインへの変更によって、実際のアプリケーションにおいて改善が見られました。
特に、幾つかの有名なウェブサイトにおいて、ユーザインタラクションテストの実行スピードの向上が見られました。
f:id:kakts:20170517001921p:plain

f:id:kakts:20170517001930p:plain

Speedometerは統合的なベンチマークツールであるが、我々は、他の統合的なベンチマークよりもSpeedometerがより実際のアプリケーションの負荷を近似していることをあきらかにしました。
Ignition-TurboFanパイプラインへの変更によって、v8のSpeedometerベンチマークが5-10%向上しました。

サーバサイドjavascriptにおいても同様に速度向上がみられ、AcmeAirとよばれるnode.jsのベンチマークにおいても10%以上の向上がみられました。

f:id:kakts:20170517002624p:plain

IgnitionとTurboFanはv8全体のメモリ使用量を減らします。 Chrome M59において、このパイプラインはデスクトップやハイエンドなモバイル端末のメモリ使用量を5-10%減らします。
このメモリ使用量削減は、以前のブログ記事*3で取り上げたIgnitionのメモリ 管理をv8にもたらしたことに起因します。

これらの改善はあくまでも序章で、IgnitionとTurboFanのパイプラインは、今後のjavascriptのパフォーマンス向上のための最適化と、ChromeとNode.jsにおけるv8のメモリ使用量の削減を容易にします。