This page contains a Japanese translation of the post on Surfin' Safari blog "Introducing SquirrelFish Extreme".
この文書について
- "Introducing SquirrelFish Extreme" の日本語訳です
- 推敲歓迎: 誤訳, タイポ, 訳語の不統一, そのほか...
SquirrelFish Extreme を紹介します
ちょうど三ヶ月前、私達は SquirrelFish を発表しました。 これは私達の JavaScript エンジンを大改造するもので、 高性能なバイトコードインタプリタを導入しました。 今日は次世代の JavaScript エンジンを公開します。 SquirrelFish Extreme (略して SFX)です。 SquirrelFish Extreme は、高速なネイティブコード生成といったより高度な技術を用い、 JavaScript に更なる性能をもたらします。
WebKit の開発をおいかけており、貢献したいと思っている方々のために、 我々の成果と、いかにそれを達成したのかについて紹介したいと思います。
どれくらい速いのか?
以下のグラフは異なるバージョンの WebKit 間で Javaの性能を示したものです。 棒が長いほど高速です。
SunSupier を一分間に何度実行できるかが指標です。 この指標を使ってグラフを書いたのは、広範な指標で性能を調べる際に 「大きいほど速い」がわかりやすいからです。見てのとおり、 現在の SquirrelFish Extreme はオリジナルの SquirrelFish のおよそ二倍、 出て一年も経たない Safari 3.0 の 10 倍も高速です。 私達はこうした改善を大変嬉しく思います。 しかしこの先の更なる改善を確信してもいます。
かなりの数の人々がこの成果に貢献してくれました。 以下では重要な仕事をした何人かに言及しますが、 JavaScript や性能の面で協力してくれた WebKit コントリビュータの皆に 感謝したいと思います。
SquirrelFish Extreme は 4 つの異なる技術を使い、 オリジナルの SquirrelFish の性能を改善しました: バイトコードの最適化、多態インラインキャッシュ、 軽量な「コンテクストスレッド」式 JIT コンパイラ、 JIT インフラに基いた新しい正規表現といった技術です。
1. バイトコードの最適化
最初に SquirrelFish を発表したとき、 その基本設計にバイトコードレベルで大いに最適化の余地があると 私達は指摘していました。Oliver Hunt、Geoff Garen、Cameron Zwarich、 そして私やその他の人々の奮闘により、 私達はそのバイトコードレベルで多くの効率よい最適化を実装しました。
私たちがやったことの一つは、オプコード内での最適化です。 JavaScript の操作はその多くが高い多態性を持ちます。 様々な場合に応じ様々に振舞いを変えるのです。 単に一番よくある、最速の場合を先にチェックするだけで、 JavaScript のプログラムはかなり高速化できるのです。
加えて、私達はバイトコードの命令セットも改善しました。 そうした改善の利点を生かした高速化をしたのです。 コンボ命令や、ピープホール最適化、定数の高速処理、 そして一般的な操作の標準ケース用にいくつか特別なオプコードを追加することもしました。
2. 多相インラインキャッシュ
SquirrelFish Extreme に施された新しい最適化の中で最も心踊るものの一つが、 多相インラインキャッシュです。これはもともと Self 言語で開発された古い技術で、 他の JavaScript エンジンで採用され良い効き目を出しています。
基本的な考え方はこうです: 言語設計の上では、JavaScript は極めて動的な言語です。 しかし大半のプログラムでは、実際のところ多くのオブジェクトが、 オブジェクト指向のクラスのような、もう少し構造のある使われ方をしています。 たとえば、多くの JavaScript ライブラリは 「x]と「y」というプロパティをもつオブジェクトを 使う作りになっています。このオブジェクト他のプロパティを持たず、 点を表現するのに使われます。多くのオブジェクトが同じ構造に基いているなら、 こうした知識を最適化に使うことができます。 動的言語コミュニティの人はこんな風に言っています。「捕まらないインチキはしてもいいんだぜ?」
では、どうやってインチキをすればいいのでしょうか? 私達はオブジェクトが同じ構造を持つこと...同じ順序で同じ名前のプロパティがあること...を検出します。 そしてオブジェクトに構造の識別子 StrucureID を割り付けます。 プロパティアクセスがおこると、初回はまず普通に(超高速のハッシュテーブルを使い)ハッシュ検索をします。 そしてプロパティをみつけた場所に StructureID とオフセット値を記録しておきます。 それ以降のアクセスでは StructureID の一致をチェックします - ふつう同じコード片は同じ構造を持つオブジェクト相手に使われるものです。 チェックに成功したらキャッシュしておいたオフセットを使い、わずか数命令で検索を行うことができます。 これはハッシュ検索よりずっと高速です。
元になったテクニックを説明した Self の論文はこれ です。 Subversion に入っている Geoff の StrucureID クラス の実装をみれば、より詳しいことがわかるでしょう。
私達の施したインラインキャッシュは、まだはじめの一歩です。 更なる高速のためにこのテクニックを改善するアイデアが沢山ありますな。 とはいえ、プロパティアクセスがボトルネックであるような性能テストでは、 現状でも劇的な改善を見ることができるでしょう。
3. コンテクトスレッド JIT
SFX に行ったもう一つの大変更はネイティブコードの生成です。 手始めに使ったのは "コンテクストスレッドインタプリタ (context threaded interpreter)" という技術です。これは少しズレた名前です。 というのは、これは単純ながら効率的な JIT コンパイラだからです。 もともとの SquirrelFish の発表で、 私達はダイレクトスレッド(direct threading)を使っていると説明しました。 これはネイティブコード生成を使わずバイトコード解釈をする形態のうち、最速のものです。 コンテクストスレッドでは次の一歩を踏み出し、いくらかのネイティブコード生成を使います。
コンテクストスレッドの基本的なアイデアは、 バイトコードを 1 オプコード毎にネイティブコードに変換するというものです。 複雑なオプコードは言語のランタイムに対する関数呼びだしになります。 単純なオプコードや、複雑なオプコードのうち高速に実行できるパスは、 ネイティブコード列へ直接差し込まれます。 まずオプコード間の制御フローは 直線的なコードとなって CPU に差し出されます。 したがってディスパッチのオーバーヘッドはなくなります。 次にもともとオプコードの間にあった多くの分岐はインライン化されます。 これは CPU の分岐予測器にとって大変予測しやすいコードになるのです。
コンテクストスレッドの基本的なアイデアを示した論文はこれです。 コンテクストスレッドの最初のプロトタイプは Gavin Barraclough が作りました。 ここ数週間でそれをチューニングし性能を磨きあげるのには、何人もが手を貸しています。
私達の軽量 JIT がもつ優れた点の一つは、ネイティブコードの生成に わずか 4000 行のコードしか使っていないところです。 コードの他の部分はクロスプラットフォームのままです。 また、これは驚くほどハックしやすいコードです。 ネイティブコードの生成がロケットサイエンスだと思っている人は考えを改めてください。 Gavin だけでなく、私達の大半はネイティブコード生成の経験がほとんどありませんでした。 しかし、えいやと書いてしまえたのです。
今のところコードは 32 ビットの x86 に限定されています。 しかしリファクタリングを行い、もっと多くの CPU アーキテクチャをサポートしていく予定です。 また 型の特殊化や、より良いレジスタ割り当てや生存区間分析(liveness analysis)を使い、 JIT の高速化をしていこうとも考えています。SquirrelFish のバイトコードは こうした変換を多く行うのに都合の良い表現となっています。
4. 正規表現 JIT
私達が JavaScript 言語本体のために最低限の JIT 基盤を作ってみたところ、 この基盤を正規表現にも適用し、正規表現のマッチングを 5 倍高速化できることがわかりました。 そこで作業を進め、実装を行いました。 全てのコードが正規表現に多くの時間を費すわけではありません。 しかし我々の新しい正規表現エンジンン WREC (the Webkit Regualr Expression Compiler) を使うと、これまで Perl や Python や Ruby で書いていたようなテキスト処理のコードを JavaScript で書けるようになります。 実際、私達の正規表現エンジンは多くのケースで他の言語で 用いているチューニング済みの正規表現に勝ると確信しています。
SunSupider ベンチマークは結構な量の正規表現を使っているため、 正規表現 JIT を開発するのは「フェアじゃない」と考える向きもあるようです。 一年前、テストに占める正規表現の割合はとても小さなものでした。 そして各 JS エンジンは正規表現以外の改善を大いに進めました。 たとえば個々の SunSpider のテストひとつのの結果が JavaScriptCore より 5 から 10 倍高速で、 Safari 3.0 版の WebKit より 70 倍速いなんてものもありました。 しかし今日まで、正規表現の性能はほとんど改善されてこなかったのです。
私達は、正規表現を高速化する方がベンチマークを変更するより良いことだと考えました。 ウェブでの実作業は沢山の正規表現を使います。 結局、JSON の検証やパースのような Web の本質的な作業は正規表現に依存しているのです。 そして John Resig の processing.js といった発展途上のテクノロジーは、 この依存を更に強めています。
ベンチマークにひとこと
ベンチマークの結果をいくつか示します。しかし能書きはありません。 WebKit の Mac 版と Windows 版の ナイトリービルドを手に入れて、 みなさん自身の手で確かめてください。
私達の使った一番目のベンチマークはは SunSupider です。 しかしどんなベンチマークもそうであるように、 SunSpider にも欠点はあります。 しかしこれは JavaScript 言語を多元的に扱い、多くの種類のコードをカバーする バランスのよいテストだと思います。テストの結果をひとつひとつ見ていけば、 異なる JavaScript 実装が異なる長所や短所を持つことがわかるでしょう。 ブラウザベンダや個人のテスト実施者がこのベンチマークを追いかけています。
次のステップと、あなたが貢献する方法
SquirrelFish Extreme のアーキテクチャには改善の余地が多く残されています。 そして私達は手助けをしてくれる開発者やテスタを歓迎します。 現状私達が調べているのは、どうやってバイトコードのインフラを使って実行時の情報を集め、 それをより良いコード生成にあてるかの方法です。 JS の関数呼び出しを高速化する方法も模索しています。 SFX のアーキテクチャの筋の良さを生かす基本的なチューニングも色々残っています。 更に他の CPU アーキテクチャ用 JIT バックエンドにも興味があります。
WebKit の JavaScript エンジンをもっと近くで見守りたい人のために squirrelfish-dev@lists.webkit.org メーリングリストを作りました。 (ここから購読できます。) FreeeNode の IRC ネットワークに #squirrelfish チャンネルもあります。 立ち寄っていただければ、更に詳しい計画や参加の方法がわかることでしょう。
試してみよう
試して、テストして、ブラウジングをしてみてください。 ナイトリービルドが入手可能です。 私達の改善があなたの Web 体験をよりよいものにできればと思います。
追記: ここ に SFX と他の先行 JavaScript エンジンの比較があります。 Charles Ying が いくつか追加でベンチマークをしてくれました。
追記2: 私達のちいさなマスコットに不満の方は、下の SquirrelFish をクリックすると WebKit ナイトリーの SVG アニメーションサポートを見ることができます。