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

Chrome 102 では、DevTools に新しい試験運用版パネル「パフォーマンス分析情報」が追加されます。この投稿では、新しいパネルの開発に取り組んでいる理由だけでなく、直面した技術的な課題とその過程で下した判断についても説明します。

ALT_TEXT_HERE

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

(パフォーマンス分析パネルを構築する理由と、このパネルを使用してウェブサイトのパフォーマンスに関する実用的な分析情報を得る方法については、こちらの動画をご覧ください)。

既存のパフォーマンス パネルは、ウェブサイトのすべてのデータを 1 か所で確認するのに便利なリソースですが、表示されるデータが多すぎるというご意見を多数いただきました。パフォーマンスの専門家でなければ、録音のどの部分に注目し、どの部分に関連性があるかを正確に判断することは困難です。

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

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

このパネルは試験運用版であり、フィードバックをお待ちしております。バグが発生した場合や、サイトのパフォーマンス向上に役立つと思われる機能のリクエストがある場合は、お知らせください。

パフォーマンス分析情報の開発方法

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

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

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

パネルに機能が追加されるにつれて、トラックもさらに追加される予定です。

当初は、これらのトラックごとに独自の <canvas> をレンダリングし、メインビューが複数のキャンバス要素が縦方向に積み重ねられたものになるようにすることを考えていました。これにより、各トラックを個別にレンダリングできるため、トラックが境界外にレンダリングされる危険性がなくなり、トラックレベルでのレンダリングが簡素化されます。ただし、このアプローチには次の 2 つの大きな問題があります。

canvas 要素は、(再)レンダリングにコストがかかります。たとえそのキャンバスが大きかったとしても、複数のキャンバスがある場合は 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)があります。これは translate で行うことができます。これは、キャンバスを指定された(x、y)位置に移動します。例:

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) でレンダリングしているかのようにトラックベースで処理でき、トラック マネージャーが各トラックをレンダリングする際に翻訳し、各トラックが前のトラックの下に正しくレンダリングされるようにします。

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

キャンバスのレンダリングは比較的負荷が高いため、分析情報パネルの操作がスムーズに行われるようにしたいと考えております。キャンバス全体を再レンダリングしなければならない場合もあります。たとえば、ズームレベルを変更した場合は、最初からやり直してすべてを再レンダリングする必要があります。キャンバスの再レンダリングは特に負荷が高い処理です。キャンバスの一部を単純に再レンダリングすることはできず、キャンバス全体を消去して再描画する必要があります。これは、ツールが必要な最小限の作業を計算し、すべてを削除して最初からやり直さない DOM 再レンダリングとは異なります。

映像の面で問題があった領域の一つは、ハイライト表示です。ペイン内の指標にカーソルを合わせると、タイムラインでその指標がハイライト表示されます。同様に、特定のイベントに関する分析情報にカーソルを合わせると、そのイベントの周囲に青い枠線が表示されます。

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

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

つまり、ハイライトを削除してもメイン キャンバスは再描画されません。代わりに、画面上のキャンバスをクリアしてから、表示されているキャンバスにメイン キャンバスをコピーします。キャンバスのコピーは低コストですが、描画は高コストです。ハイライトを別のキャンバスに移動することで、ハイライトのオンとオフを切り替えるときにかかるコストを回避できます。

トレース解析の包括的なテスト

新機能をゼロから構築するメリットの一つは、以前に行った技術的な選択を振り返って改善できることです。改善したかったことの一つは、コードをほとんど完全に異なる部分に明示的に分割することでした。

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

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

また、DevTools で録画を取得し、保存してからテストスイートの一部として読み込むことで、トレース解析を包括的にテストすることもできました。実際のトレースでテストでき、古くなる可能性がある偽のトレースデータを大量に構築せずに済むため、これは素晴らしいことです。

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

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

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

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

まとめ

新しい [パフォーマンス分析情報] パネルの構築は、チームにとって非常に楽しく、有益な経験となりました。トレース ファイルやキャンバスの操作などについて、多くのことを学びました。新しいパネルをお試しいただき、ぜひフィードバックをお寄せください。

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

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

デフォルトの開発用ブラウザとして Chrome の CanaryDevBeta を使用することを検討してください。これらのプレビュー チャンネルでは、最新の DevTools 機能にアクセスしたり、最先端のウェブ プラットフォーム API をテストしたりできます。また、ユーザーよりも早くサイトの問題を見つけることもできます。

Chrome DevTools チームに問い合わせる

以下のオプションを使用して、新機能、アップデート、DevTools に関連するその他の内容について話し合います。