パフォーマンス分析情報を作成した方法とその理由

Chrome 102 では、新しい試験運用版パネルであるパフォーマンス分析情報が DevTools に追加されます。この投稿では、新しいパネルに取り組む理由だけでなく、私たちが直面した技術的な課題や、その過程で行った決定についてお話しします。

ALT_TEXT_HERE

別のパネルを作成する理由

(まだご覧になっていない場合は、パフォーマンス分析情報パネルを作成する理由と、パネルを使用してウェブサイトのパフォーマンスに関する行動につながるインサイトを得る方法に関する動画を投稿しました)。

既存のパフォーマンス パネルは、ウェブサイトのすべてのデータを 1 か所で確認したい場合に最適なリソースですが、少し戸惑ってしまうような気がしました。パフォーマンスのエキスパートでなければ、何を見て、録音のどの部分に関連性があるかを正確に把握するのは困難です。

[Insights] パネルを開くと、トレースのタイムラインを表示してデータを検査できるほか、DevTools で掘り下げてみる価値のある主要な「分析情報」の便利なリストも表示できます。Insights では、レンダリング ブロック リクエスト、レイアウト シフト、時間のかかるタスクなど、ウェブサイトのページ読み込みのパフォーマンス、特にサイトの Core Web Vitals(CWV)スコアに悪影響を及ぼす可能性のある問題を特定できます。パフォーマンスの分析情報では、問題を報告するだけでなく、CWV スコアの改善に役立つ実用的な提案や、詳細なリソースやドキュメントへのリンクも提示されます。

パネルのフィードバック リンク

このパネルは試験運用版です。ぜひフィードバックをお寄せください。バグに遭遇した場合や、サイトのパフォーマンス向上に役立ちそうな機能リクエストがある場合は、お知らせください。

パフォーマンス分析情報の構築方法

他の DevTools と同様に、パフォーマンス分析情報を TypeScript で作成し、lit-html を基盤とするウェブ コンポーネントを使用してユーザー インターフェースを構築しました。パフォーマンス分析情報が異なっている点は、メインの UI インターフェースがHTML canvas 要素であり、タイムラインがこのキャンバスに描画される点です。多くの複雑さの原因は、このキャンバスを管理することです。適切な細部を適切な場所に描画するだけでなく、マウスイベント(たとえば、ユーザーがキャンバスをどこでクリックしたか、描画したイベントをユーザーがクリックしたかなど)を確認し、キャンバスが効果的に再レンダリングされるようにします。

1 つのキャンバス上の複数のトラック

特定のウェブサイトについて、レンダリングする複数の「トラック」があり、それぞれが異なるカテゴリのデータを表します。たとえば、分析情報パネルには、デフォルトで次の 3 つのトラックが表示されます。

引き続きパネルに機能をリリースしていく中で、さらに多くのトラックが追加される予定です。

当初は、これらのトラックごとに独自の <canvas> をレンダリングし、メインビューが垂直方向に積み重ねられた複数のキャンバス要素になることにしました。この方法では、各トラックを個別にレンダリングでき、境界外でトラックがレンダリングされる恐れがないため、トラックレベルでのレンダリングが簡単になります。しかし、この方法には 2 つの大きな問題があります。

canvas 要素は(再)レンダリングするためコストがかかります。キャンバスが複数ある場合は、キャンバスが 1 つでも大きくても、1 つのキャンバスよりもコストがかかります。複数のトラックにまたがるオーバーレイ(たとえば、FCP 時間などのイベントを示す縦線)のレンダリングは複雑になります。複数のキャンバスにレンダリングし、すべてが一緒にレンダリングされ、適切に配置されていることを確認する必要があります。

UI 全体に 1 つの canvas を使用するには、各トラックが正しい座標でレンダリングされ、別のトラックにオーバーフローしないようにする方法を見つける必要がありました。たとえば、特定のトラックの高さが 100 ピクセルの場合、120 ピクセルの高さをレンダリングして、その下のトラックにはみ出すことはできません。この問題を解決するには、clip を使用できます。各トラックをレンダリングする前に、可視トラック ウィンドウを表す長方形を描画します。これにより、これらの境界の外に描画されたパスはキャンバスによってクリップされます。

canvasContext.beginPath();
canvasContext.rect(
    trackVisibleWindow.x, trackVisibleWindow.y, trackVisibleWindow.width, trackVisibleWindow.height);
canvasContext.clip();

また、各トラックが垂直方向の位置を認識する必要がないようにする必要があります。各トラックは、それ自体を(0, 0)でレンダリングしているかのようにレンダリングし、トラックの全体的な位置を管理する上位レベルのコンポーネント(TrackManager)を用意しています。そのためには、キャンバスを指定された(x, y)位置で移動させる translate を使用します。次に例を示します。

canvasContext.translate(0, 10); // Translate by 10px in the y direction
canvasContext.rect(0, 0, 10, 10); // draw a rectangle at (0, 0) that’s 10px high and wide

rect コードで 0, 0 を位置として設定しているにもかかわらず、適用された変換全体により、長方形は 0, 10 でレンダリングされます。これにより、トラックごとに(0, 0)でレンダリングしているかのように作業でき、各トラックをレンダリングする際にトラック マネージャーが翻訳して、各トラックが前のトラックの下に正しくレンダリングされるようにできます。

トラックとハイライトのオフスクリーン キャンバス

キャンバスのレンダリングは比較的コストがかかるため、Google では、ユーザーが操作する際に [分析情報] パネルをスムーズで応答性の高い状態にしたいと考えています。場合によっては、キャンバス全体を再レンダリングすることは避けられないことがあります。たとえば、ズームレベルを変更すると、最初からすべてを再レンダリングする必要があります。キャンバスの再レンダリングは、そのごく一部を再レンダリングするだけではなく、キャンバス全体をワイプして再描画する必要があるため、特にコストがかかります。これは、ツールが必要最小限の作業量を計算し、すべてを削除して最初からやり直すことができない DOM 再レンダリングとは異なります。

視覚的な問題が生じた領域の 1 つがハイライト表示されていました。ペインで指標にカーソルを合わせると、タイムライン上で指標がハイライト表示されます。同様に、特定のイベントの分析情報にカーソルを合わせると、そのイベントの周囲に青い枠線が描画されます。

この機能はまず、ハイライト表示をトリガーする要素上でのマウス移動を検出し、そのハイライトをメイン キャンバスに直接描画することで実装されました。ハイライト表示を削除しなければならないときに問題が発生します。唯一の選択肢は、すべてを再描画することです。ハイライトがある領域を再描画することは(アーキテクチャを大幅に変更せずにはいけません)、キャンバス全体を再描画することは不可能です。1 つのアイテムの周りの青い枠線を削除したいからといって、やりすぎに感じたからです。また、マウスをさまざまなアイテムに素早く動かして、複数のハイライトを連続してトリガーすると、視覚的な遅延が発生します。

この問題を解決するために、UI を 2 つの画面外のキャンバスに分割します。トラックをレンダリングする「メイン」キャンバスと、ハイライトを描画する「ハイライト」キャンバスです。次に、これらのキャンバスを画面上でユーザーに表示される単一のキャンバスにコピーしてレンダリングします。キャンバス コンテキストで drawImage メソッドを使用すると、別のキャンバスをソースとして使用できます。

つまり、ハイライトを削除してもメイン キャンバスは再描画されません。代わりに、画面上のキャンバスをクリアしてから、表示されているキャンバスにメイン キャンバスをコピーできます。キャンバスをコピーする操作はコストがかかり、描画の処理に費用がかかります。そのため、ハイライトを別のキャンバスに移動することで、ハイライトのオン / オフ時にコストが発生しないようにしています。

包括的にテストされたトレース解析

新機能をゼロから構築する利点の 1 つは、これまでに行った技術的な選択を振り返り、改善できることです。改善したかったことの一つは、コードをほぼ完全に異なる 2 つの部分に明示的に分割することでした。

トレース ファイルを解析し、必要なデータを取得します。トラックのセットをレンダリングします。

解析(パート 1)を UI 処理(パート 2)から分離することで、堅牢な解析システムを構築できました。各トレースは、さまざまな懸念事項を担当する一連のハンドラを介して実行されます。LayoutShiftHandler はレイアウト シフトに必要なすべての情報を計算し、NetworkRequestsHandler はネットワーク リクエストの抽出のみに取り組みます。トレースのさまざまな部分を担当する複数のハンドラがあるこの明示的な解析ステップも有益です。トレースの解析は非常に複雑になり、一度に 1 つの問題に集中できます。

また、DevTools で記録を取得して保存し、テストスイートの一部として読み込むことで、トレース解析を包括的にテストできました。これは、実際のトレースでテストでき、古くなる可能性がある架空のトレースデータを大量に構築しないため、非常に便利です。

キャンバス UI のスクリーンショット テスト

テストの話題に移り、通常はフロントエンド コンポーネントをブラウザにレンダリングし、期待どおりに動作することを確認することで、フロントエンド コンポーネントをテストします。クリック イベントをディスパッチして更新をトリガーし、コンポーネントが生成する DOM が正しいことをアサートできます。このアプローチは、私たちにとってはうまく機能しますが、キャンバスへのレンダリングを検討する場合、うまくいきません。キャンバスを調べて、そこに描画される内容を決定する方法はありません。そのため、レンダリングしてからクエリを実行する通常のアプローチは適切ではありません。

テスト カバレッジを広げるために、スクリーンショット テストを導入しました。各テストはキャンバスを起動し、テストするトラックをレンダリングしてから、キャンバス要素のスクリーンショットを撮ります。このスクリーンショットはコードベースに保存されます。その後のテスト実行では、保存されたスクリーンショットと生成されたスクリーンショットが比較されます。スクリーンショットが異なる場合、テストは失敗します。レンダリングを意図的に変更してテストの更新が必要になった場合に、テストを実行してスクリーンショットを強制的に更新するためのフラグも用意されています。

スクリーンショット テストは完璧ではなく、やや難解です。テストできるのは、具体的なアサーションではなく、コンポーネント全体が期待どおりにレンダリングされるかどうかのみです。当初は、すべてのコンポーネント(HTML またはキャンバス)が正しくレンダリングされることを保証するために、過剰なアサーションを使用していました。これにより、テストスイートが大幅に遅くなり、ほとんど無関係な UI の微調整(わずかな色の変更、アイテム間のマージンの追加など)によって複数のスクリーンショットが失敗し、更新が必要になるという問題が生じました。現在は、スクリーンショットの使用を縮小し、キャンバス ベースのコンポーネント専用に使用していますが、これまでのところ、このバランスでうまく機能しています。

おわりに

新しい [パフォーマンス分析情報] パネルの構築は、チームにとってとても楽しく、教育的な経験となりました。トレース ファイル、キャンバスの操作など、多くのことを学びました。新しいパネルがお役に立てば幸いです。フィードバックをお待ちしております。

[パフォーマンス分析情報] パネルについて詳しくは、パフォーマンス分析情報: ウェブサイトのパフォーマンスに関する実用的な分析情報を取得するをご覧ください。

プレビュー チャネルをダウンロードする

Chrome CanaryDevBeta を既定の開発ブラウザとして使用することをご検討ください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたり、ユーザーが実際に体験する前にサイト上の問題を検出したりできます。

Chrome DevTools チームへのお問い合わせ

投稿内の新機能や変更点、または DevTools に関するその他のことについて話し合うには、次のオプションを使用します。

  • crbug.com からご提案やフィードバックをお送りください。
  • DevTools の問題を報告するには、DevTools でその他のオプション アイコン その他   > [ヘルプ] > [DevTools の問題を報告する] を選択します。
  • @ChromeDevTools にツイートします。
  • 「DevTools の新機能」の YouTube 動画または DevTools のヒントの YouTube 動画でコメントを残してください。