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

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

ALT_TEXT_HERE

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

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

既存のパフォーマンス パネルは、ウェブサイトのすべてのデータを 1 か所で確認したい場合に最適なリソースですが、少しわかりにくいと感じる方もいらっしゃるかもしれません。パフォーマンスの専門家でない場合、何を探すべきか、録画のどの部分が関連しているかを正確に把握することは困難です。

[分析情報] パネルでは、トレースのタイムラインを表示してデータを検査できるだけでなく、DevTools が掘り下げるべき主な「分析情報」と見なしたものの便利なリストも取得できます。インサイトでは、レンダリングをブロックするリクエスト、レイアウト シフト、長時間タスクなどの問題が特定されます。これらの問題は、ウェブサイトのページ読み込みパフォーマンス、特にサイトのコア ウェブ バイタル(CWV)スコアに悪影響を及ぼす可能性があります。パフォーマンス分析情報では、問題のフラグ付けに加えて、CWV スコアを改善するための実用的な提案が提供され、関連するリソースやドキュメントへのリンクも表示されます。

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

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

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

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

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 と呼びます)を用意しました。これは、指定された(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)でレンダリングしているかのように作業し、トラック マネージャーが各トラックをレンダリングする際に変換して、各トラックが前のトラックの下に正しくレンダリングされるようにします。

トラックとハイライトの画面外キャンバス

キャンバスのレンダリングは比較的コストが高いため、インサイト パネルがスムーズに動作し、操作にすばやく反応するようにしています。キャンバス全体を再レンダリングしなければならない場合もあります。たとえば、ズームレベルを変更した場合は、最初からやり直してすべてを再レンダリングする必要があります。Canvas の再レンダリングは、その一部だけを再レンダリングすることができず、Canvas 全体を消去して再描画する必要があるため、特にコストがかかります。これは、必要な最小限の作業をツールで計算し、すべてを削除して最初からやり直す必要がない DOM の再レンダリングとは異なります。

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

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

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

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

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

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

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

解析(パート 1)と UI 作業(パート 2)を分離することで、堅牢な解析システムを構築できました。各トレースは、さまざまな懸念事項を担当する一連のハンドラを通過します。LayoutShiftHandler はレイアウト シフトに必要なすべての情報を計算し、NetworkRequestsHandler はネットワーク リクエストの抽出のみを行います。トレースのさまざまな部分を担当するさまざまなハンドラがある明示的な解析ステップがあることもメリットです。トレースの解析は非常に複雑になる可能性があるため、一度に 1 つの懸念事項に集中できると便利です。

また、DevTools で録画して保存し、テストスイートの一部として読み込むことで、トレース解析を包括的にテストすることもできました。実際のトレースでテストできるため、大量の偽のトレースデータを構築して、それが古くなるのを防ぐことができます。

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

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

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

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

まとめ

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

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

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

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

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

DevTools の新機能、更新、その他の関連事項について話し合うには、次のオプションを使用します。