Google I/O 2021 フロントエンドまとめ(5/6)〜 読み込み後のパフォーマンスについて(2/2)〜
この記事は、Google I/O 2021で発表されたフロントエンド関連情報の中で個人的に気になったものをまとめたもので、
全6記事中5本目。
読み込み後のパフォーマンス の話
読み込み後のパフォーマンス @Google I/O 2021
パフォーマンス最適化のためには何をやっていくか。というお話
挙げられたのは、以下5つ。
- Measure First(まず測ろう)
- Free up the main thread(メインスレッドを空けよう)
- Minimize memory usage(メモリ使用量を最小化しよう)
- Load with best-practice(最適な手法でロードしよう)
- Ship modern code(コードをモダン化しよう)
Measure First(まず測ろう)
パフォーマンス上の問題を語るときは、まず実際に計測してから。というお話。(全力同意。)
そしてより踏み込んだ話として、「ユーザから(計測)データを収集するのが最善」と語っている。その根拠としては、「人工のデータを測定する方法ではユーザが実際に経験すること正確に把握することはでき」ず、「データに基づいて最適化を行う」ために「ユーザからデータを収集するのが最善」とのこと。
Free up the main thread(メインスレッドを空けよう)
メインスレッドで実行されるコード量を減らして、メインスレッドを軽量化しよう。というお話。
というのも、メインスレッドはレンダリング処理(スタイル計算、レイアウト、描画、etc.)にも使われるスレッドのため、このスレッドを重くするとこれらの処理に直接影響を与えることになる。
したがって理想的なメインスレッドの使用方法として、このスレッドを最も重く中断不可能な処理であるレンダリングに専心させるのが良いとのこと。
そしてその一つの手段として、WebWorkerを使ってアプリのコードの一部を別スレッドに移すことを挙げている。
Workerは構文解析や分析など重い処理に適しており、Workerにコードを移動する際に注意事項として、そのコードがUIの即時変更に関与しないようにすること(UI変更する処理とは分離されているべき)とのこと。
さらに良くないこととして、Workerの処理が終わるまでユーザに操作のフィードバック(となるUI上の変化)が返ってこないことと語っている。
また、Workerへのコードの移動が難しくても最近登場した「comlinkを使う」という優れた解決策で、重いモジュールをメインスレッドからワーカースレッドへ移しつつ使い慣れたAPIをそのまま利用できるとのこと。
さらにWebWorkerとは全く別の手段として、Paint Workletを紹介しているが、こちらはChromeを始めとしたblink系ブラウザでしかまだサポートされてないので、こちらはまぁ、「こんなのもありますよ」っていう紹介してみただけのお話っぽい。
Minimize memory usage(メモリ使用量を最小化しよう)
Webアプリケーションのメモリ使用量の最小化が滑らかさと応答性に影響を与えるということはわかっているけれども、いざこれを定量化しようとなるとそれは困難で、というのも、Webでメモリを割り当てて維持する方法は多岐にわたり、性能に与える影響がそれぞれ異なるからだとしている。
また、しきい値や影響度の程度もハードウェアや他のソフトウェアなど外的要因にも左右されうることにも言及する。
しかしこれまでの調査からいくつかの一般的な結論が得られているとのことで、
たとえばメモリ使用量が合計ブロック時間(Total Blocking Time, TBT; メインスレッドが初期描画を一旦終えてからユーザの操作を受け付けられるようになるまでのブロックされていた総合計時間。Time to Interactive(TTI) - First Contentful Paint(FCP)で求められる)に与える影響の調査から、メモリ使用量には性能低下を招くしきい値があることがわかったとのこと。
そしてこれは過度のメモリ使用を避ける必要性を示しているとのこと。
- メモリ容量を無尽蔵とか思ったりせず、大きなデータのむやみなロードは避け、メモリ使用量を節約しましょう
- またデータがどのようにキャッシュすべきか(メモリ?orディスク?)について考慮・検討してみましょう
- メモリリークを起こしていないかメモリを監視しましょう(→解説記事)
Load with best-practice(最適な手法でロードしよう)
「必要物のみ(適切なタイミングで)ロードせよ」というお話。
具体的には以下とのこと。
- code-split JS to load as-needed: コードスプリッティング を施し、必要なときに必要な分だけJSがロードされるようにする
- preload in idle time: アイドル時にリソースをpreloadするようにして、デバイスの空き時間を有効活用する
- defer invisible content: 直ちに使われないリソースの読み込みを後回しにする
- lazy-load embeds: 画面上の可視範囲外の画像などにはlazy-loadを施す
- avoid inlining assets into JS: JS内に(画像などの)アセットをインライン記述しない
Ship modern code(コードをモダン化しよう)
(常に)モダンなコードが配布されているようにしましょうね。というお話。
曰く、「新しいJavaScriptにはブラウザの90〜95%が対応しています」として、なお、その残りの10〜5%に含まれてる"I"で始まって"E"で終わる 問題のアレ
通常はトランスパイルされたpolyfilコードよりもネイティブAPIの方が高速に動作するうえ、その冗長なpolyfilコードが削減されることによりロードするコストも削減されると説明している。
さらに、古いブラウザもサポートしつつモダンなコードを配布する手段として、nomodule
を使ってフォールバックバンドルを提供する方法を提案している。(なお、やはり問題のアイツ)
そして最後に「しばらくトランスパイラの設定を見直していない場合はそこからロード後性能の改善を始めるべきです」との言葉で締めている。