V8JsのスナップショットでSSRを高速化

[V8Js](http://php.net/manual/ja/book.v8js.php)はPHPの機能拡張で[Google V8 JavaScript Engine](https://ja.wikipedia.org/wiki/Google_V8_JavaScript_Engine)をPHPに組み込みます。PHPから直接JSのコードが実行できます。

V8はとても高速ですが、PHPから利用する場合にそのインスタンスの生成コストとサイズの大きいJSライブラリの初期化のコストが気になります。

#V8のスナップショット

V8のスナップショットの機能を使うと、Reactなどのライブラリコードやアプリケーションのコードがbuilt-inのコードとしてV8の初期状態から利用可能になります。PHPのV8Jsも対応していて、大幅なパフォーマンスアップが可能です。

> snapshot がコンパイル済みの built-in の JavaScript のコードを含むシリアル化されたヒープを持つため、最初の context を作る時間は高度に最適化される。extensive caching は GC と同様に V8 のパフォーマンスのキーである。 - https://javascript.g.hatena.ne.jp/edvakf/20100407/1270626241


![image](https://qiita-image-store.s3.amazonaws.com/0/3940/fdee6616-d903-cc8f-3222-21aab6b2fe8d.png)
http://stesie.github.io/2016/02/snapshot-performance

このグラフはV8Jsの作者の[stesie](https://twitter.com/stesie23)さんのブログ [20x performance boost with V8Js snapshots](http://stesie.github.io/2016/02/snapshot-performance) からのものです。

5.0.104-bassnapの青いバーがV8Jsインスタンス生成するコストで、赤いバーがReactJsのコードの読み込みコストです。スナップショットを使えば、読み込みコストがnewの時に発生するためインスタンス生成コストはやや上昇しますが、Reactのコードのコストは無くなります。（一番右）。縦軸はミリ秒で、PHPからのJSの利用コストがとても低いことがわかります。

## スナップショットの種類

スナップショットは外部にもつか内部に持つか、２つの種類があります。パフォーマンスに優れるのは内部ですが、V8のコンパイル時にJSのコードから出力したスナップショットを含める必要があります。詳しくは[README](https://github.com/phpv8/v8js/blob/php7/README.Linux.md#snapshots)をご覧ください。デフォルトは外部です。

## スナップショット作成

外部のスナップショットの作成は`createSnapshot()`で行います。PHPのマニュアルには無いですが、Githubの[READMEのPHP API](https://github.com/phpv8/v8js#php-api)に記載されています。

```php
$blob = V8Js::createSnapshot(file_get_contents(__DIR__ . "/react.bundle.js"));
file_put_contents(__DIR__ . '/snapshot_blob.bin', $blob);
```

## スナップショット読み込み

```php
$blob = file_get_contents(dirname(__DIR__, 2) . '/tmp/snapshot_blob.bin');
$v8 = new V8Js('PHP', [], [], true, $blob);
```

これも[PHPのマニュアル V8Js::__construct](http://php.net/manual/ja/v8js.construct.php)には4つまでの引数しかありませんが、5つ目に`V8Js::createSnapshot`で作成した`$snapshot`を渡します。


## テスト

テストコードを作成してみました。

https://github.com/koriym/v8js-benchmark

```

new V8Js without snapshot ........ 6.529 msec .. 4,252.041 points ....... 71.56% ......... 1.00x
new V8Js with snapshot ........... 9.124 msec .. 5,941.913 points ...... 100.00% ......... 1.40x

executeString() with snapshot ........... 0.174 msec .... 117.127 points ........ 2.52% ......... 1.00x
executeString() without snapshot ........ 6.911 msec .. 4,646.427 points ...... 100.00% ........ 39.67x

V8 + internal snapshot ......... 7.459 msec .. 4,961.625 points ....... 10.41% ......... 1.00x // simulated
V8 + external snapshot  ....... 26.858 msec . 17,866.169 points ....... 37.48% ......... 3.60x
V8 ............................ 71.668 msec . 47,674.422 points ...... 100.00% ......... 9.61x
```

executeString()では40倍と大きな差はありますが、これはReactのコードをこの時点で実行しているためです。"internal snapshot"は、内部snapshotがあるV8と素のV8を同じ生成コストと仮定しての値です。外部スナップショットの3.6倍早く、スナップショットを使わない場合の約10倍速い数値になっています。

## 結論

PHPの`V8Js`は決して実験レベルのものではなく、スナップショットの機能を用いれば驚異的な低オーバーヘッドで２つの言語を繋ぐ優れたPHP拡張です。expressでサーバーを起動する必要がなく、スカラー値のみならず、オブジェクトやクロージャーを直接双方向に渡すことができます。

アクティブにメンテナンスされてる作者の[Stefan Siegl](https://twitter.com/stesie23)さんに感謝。ReactJSやVue.jsのSSRに使用するだけはなく、豊富なJSのライブラリと高速なV8エンジンを活用してPHPと新しく創造的な連携ができるのではないでしょうか。

こちらの投稿もご覧ください。

 * [PHPのV8JsでSSR](http://qiita.com/koriym/items/2b9d0eeb68ca544f84b7)