ChromiumにおけるJavaScriptのコードキャッシュについて

# はじめに
この記事は[Chromium Browser Advent Calendar 2017](https://qiita.com/advent-calendar/2017/chromium)の22日目の記事になります。

ChromiumプロジェクトでFetch APIやService Worker周りを担当しているhoroです。つい先日、[Microsoft Edgeの開発者版](https://blogs.windows.com/msedgedev/2017/12/19/service-workers-going-beyond-page/)や[Safariの開発者版](https://webkit.org/blog/8042/release-notes-for-safari-technology-preview-46/)でもService Workerが使えるようになったそうで、非常に嬉しい限りです。

さて、今回は、Service Workerにも少し関係がある、JavaScriptのコードキャッシュについてです。

# コードキャッシュとは

[この資料](https://docs.google.com/presentation/d/1b-ALt6W01nIxutFVFmXMOyd_6ou_6qqP6S0Prmb1iDs/present?slide=id.g2220ee5730_1_7)によると、現実世界のWebページでは、V8の実行時間のうちJavaScriptのパースに15-20%の時間がかかっていると言われています。

そこで、Chromiumでは、頻繁に読み込まれるJavaScriptファイルに関して、パース処理によって生成されるバイトコードをキャッシュしておくことで、次回以降の実行を高速化しています。

具体的には、現在(Chrome 63)のところ、下記のタイミングでキャッシュを作成しています。

 1. HTTPCacheに入っているJavaScriptファイルのコードキャッシュは、72時間以内に2回読み込まれた場合に作成。[^1]
 2. Service WorkerのJavaScriptファイルのコードキャッシュは、Service Workerのインストール時に作成。[^2]
 3. CacheStorageに入っているJavaScriptファイルのコードキャッシュは、Service Worker経由でページに初回ロードされた時に作成。[^3]


# DevToolsで挙動を確認する

DevToolsを使うことで、コードキャッシュがいつ作成されて、いつ利用されているかを確認することができます。

Chrome 64以降[^4]のDevToolsでPerformanceタブを開き、左上にあるリロードボタンを押し、ページが読み込まれたら適当なタイミングでStopボタンを押す。そして、キーボードのWASDキーを駆使してCompile Scriptと書かれたブロックを探し出しクリックすると下記のように詳細情報が表示されます。この場合、316,480バイトのコードキャッシュが利用されたということを意味しています。

![codecachedevtools.png](https://qiita-image-store.s3.amazonaws.com/0/110601/2e07cbde-ffb5-b55f-b802-3325add48e5a.png)

コードキャッシュが作成された場合、Produced Cache Sizeとして、作成されたコードキャッシュのサイズが表示されます。



# フルコードキャッシュ

V8のパーサーはFull("eager")パーサーとFast("lazy")パーサーの2種類があり、現在のところ、バイトコードのキャッシュはFastパーサーで作成されたバイドコードを保存しています。FullパーサーはJavaScriptファイル全体をパースするのですが、Fastパーサーは一部をスキップすることで高速にパース処理を行っています。詳細は[こちら](https://docs.google.com/presentation/d/1b-ALt6W01nIxutFVFmXMOyd_6ou_6qqP6S0Prmb1iDs/present?slide=id.g1d5daf2403_0_153)を参照。


上に挙げた1.のHTTPCacheに入っているJavaScriptファイルに関しては通常のページロード時に重い処理を行うのはパフォーマンス的に得策ではないので、Fastパーサーを使うのが妥当であると考えられます。しかし、2.のService WorkerのJavaScriptファイルに関しては、Service Workerのインストールは別スレッドで実行されるので多少重い処理をしても問題ないのではないか？また、3.のCacheStorageに入っているJavaScriptファイルに関しても、Service Workerのインストール時にCacheStorageに保存されるJavaScriptファイルはFullパーサーでコードキャッシュを作成してもいいのではないか？という話になりました[^5]。

そこで、Service Workerのインストール時にCacheStorageにJavaScriptファイルが保存される際に、Fullパーサーを使ったコードキャッシュを作成するようにして、手元にあるNexus 4で実験を行ったところ、Twitter mobileのサイトのFirst meaningful paint時間が[1秒以上改善されました](https://docs.google.com/document/d/1dNKBiXi_aQTx6mMiHLLzuTZuWLsxTnejFK8GeLffCI4/edit#heading=h.b20tff15si49)。


この結果を受けて、下記の2つのフラグをChrome 64から導入し、実験を行っていました。 

 - chrome://flags/#enable-service-worker-script-full-code-cache
  - Service WorkerのJavaScriptファイルのコードキャッシュがFullパーサーで作成されるようになります。
  - Service Workerの次回以降の起動が高速化されることが期待されます。
 - chrome://flags/#enable-pwa-full-code-cache
  - Service Workerのインストール時にCacheStorageにJavaScriptファイルが保存される際に、Fullパーサーを使ったコードキャッシュが作成されるようになります。
  - CacheStorageに保存されたJavaScriptファイルがページでロードされた際に高速に実行されるようになることが期待されます。

実験の結果が良かったので、Chrome 65で[2018年4月10日ごろにデフォルトで有効になりました](https://crbug.com/768705#c36)。
が、バグが発見されたため、[Chrome 67に延期されました](https://crbug.com/768705#c40)。


[^1]: https://crbug.com/399580
[^2]: https://crbug.com/449895
[^3]: https://crbug.com/581613
[^4]: https://crbug.com/769166#c20
[^5]: http://crbug.com/768705