PageSpeed Insightsの各評価指標について調べてみました

この記事は「[LCL Advent Calendar 2020](https://qiita.com/advent-calendar/2020/lcl)」14日目の記事です。


## はじめに

WebサイトやWebサービスにとって、パフォーマンスの計測・改善は、ユーザーにより良い体験を提供するために必要不可欠な要素です。

特に2021年5月からは[Core Web Vitalsの各指標がgoogle検索のランキング要因に組み込まれる](https://developers.google.com/search/blog/2020/05/evaluating-page-experience
)ことが既に発表されており、今後はより多くのWebサイトがWebパフォーマンスを意識する必要が出てくることが予想されます。

とは言いつつ、私自身も業務の中で「あれ？この指標ってなんだっけ？」となることが多かったりします。

ということで、本記事ではWebパフォーマンス改善ツールの中からPageSpeed Insightsを題材として、そこで用いられている各評価指標とそれの意味するところについて簡単に紹介していきます。

なお、各評価指標の改善方法については実際にPageSpeed Insightsを用いてWebパフォーマンスを測定した際に提示される改善方法に従うということで、本記事では割愛させていただきます。


## 前提

### PSI: PageSpeed Insights
[PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/?hl=ja)

Googleが提供するWebパフォーマンス測定ツールです。指定したサイトのパフォーマンスに関するレポートと、改善方法を確認することができます。


### ラボデータ・フィールドデータ
#### ラボデータ
固定された条件でページ読み込みをシミュレートしたデータです。
[PSIではLighthouseを使用して指定されたURLを分析し、さまざまな指標についてページのパフォーマンススコアを生成しています。](https://developers.google.com/speed/docs/insights/v5/about)

- （追記）3G回線による表示速度をエミュレートしているそうです。

#### フィールドデータ
特定のURLについて、実際の様々な端末やネットワーク条件で閲覧したユーザーから匿名で収集したパフォーマンスデータです。
PSIでは指定したURLを[Chrome User Experience Report(CrUX)](https://developers.google.com/web/tools/chrome-user-experience-report)データセットから検索しています。

- PSIではフィールドデータとして過去28日間の収集結果を使用しているので、Webパフォーマンスを改善してもフィールドデータに反映されるまである程度時間がかかります。

- ラボデータとフィールドデータとでは測定条件が異なることから、結果に差異がある場合があります。


## FCP: First Contentful Paint
参考
https://web.dev/fcp/
https://web.dev/first-contentful-paint/

First Contentful Paintは<b>ページの読み込みを開始してから、ブラウザがページのコンテンツのいずれかの部分を画面にレンダリングするまでの時間</b>の指標です。ここでのコンテンツとは、テキスト、画像（背景画像を含む）、`<svg>` 要素、または白ではない`<canvas>`要素を指します。

たとえば以下の読み込みタイムラインでは、最初のテキスト要素と画像要素が画面にレンダリングされる 2 番目のフレームで FCPが発生します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/bb54bc6c-21f3-1aa7-e197-bc9f81f5f92f.png)

### 測定方法
フィールドデータもしくはラボデータを用いて測定されます。

## SI: Speed Index
参考
https://web.dev/speed-index/
https://github.com/WPO-Foundation/webpagetest-docs/blob/master/user/Metrics/SpeedIndex.md

Speed Indexは<b>ページの可視部分がどのくらいの速さで表示し終えるか</b>の指標です。
単純な「表示し終えるまでの時間」だけでなく、<b><span style="color: #ff0000">表示し終えるまでの過程</span></b>も評価されます。

[WebPageTest.org](https://github.com/WPO-Foundation/webpagetest-docs/blob/master/user/Metrics/SpeedIndex.md)で使用されている例を用いて説明していきます。
たとえば

- 全体のほとんどが最初に表示され、最後に残りが表示されるページ（下画像上部）
- 全体のほとんどが最後に表示されるページ（下画像下部）

の二つのページがあるとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/5c3dc32c-7f12-3f06-df9a-acf4318370ad.png)


この二つのページについて「経過時間に対するページの完成度」をプロットすると、次のようなグラフを描くことができます。

<img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/f381b1f2-b70f-1651-106a-dc262127eca2.png">

このグラフに対して両曲線の下の面積を計算することで、「ページの表示状況」を数値に変換します。

<img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/9fcdfe15-2cfb-9a48-7d21-753fbd42f4d3.png"> <img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/c8a5abd4-73db-6034-be12-535cba292d1b.png">

一方で「表示し終えていない部分」は、この面積の逆の領域で表すことができます。

<img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/c1640b0b-9a68-98e9-7c45-1cfc1b5dc10e.png"> <img width="400" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/838ee9b1-4ee9-c975-5550-9f8bcef1b0a6.png">


この値がSpeed Indexに相当する値です。つまり、Speed Indexは<b>値が低いほど良い</b>ということになります。

式で表すと下記のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/799575d4-f431-0a0e-316e-f492b6bca192.png)

このSpeed Index算出式には単位がついていない一方で、PageSpeed InsightsのSI指標にはmsの単位が付与されています。[lighthouseのissue](https://github.com/GoogleChrome/lighthouse/issues/6130)では、（v2→v3アップデートにおいて）単位が追加された理由や、v3.1.0でさらに単位がmsからsに変更された理由などが説明されています。

### "ページの完成度"の算出方法
lighthouseではブラウザ上でページが読み込まれている様子を動画でキャプチャし（[執筆時点では毎秒10フレーム](https://github.com/WPO-Foundation/webpagetest-docs/blob/master/user/Metrics/SpeedIndex.md)）、フレーム間の視覚的な進行を計算した後、Speedline Node.jsモジュールを使用してSpeed Indexスコアを生成しています。


-  SpeedlineはWebpageTest.orgが紹介しているオリジナルのSpeed Indexと同じ原理を使用しています。  

- ただしページ読み込みの進行度合を計算するためにWebpageTest.orgがヒストグラム距離を使用しているのに対し、lighthouseのspeed IndexではSSIM(Structural Similarity)を使用しています。

  - ヒストグラム距離: 開始ヒストグラム（最初のビデオフレーム）と終了ヒストグラム（最後のビデオフレーム）の差を計算し、その差をベースラインとして使用、動画の各フレームのヒストグラムの差をベースラインと比較して、その動画フレームがどれだけ完成しているかを判断
  - SSIM: 構造的類似性 画像処理の分野でそういう指標があるという話なので、ここでは省略します。

### 測定方法
ラボデータを用いて測定されます。

## LCP: Largest Contentful Paint
参考
https://web.dev/lcp/
https://web.dev/lighthouse-largest-contentful-paint/

LCPは<b>ビューポート内に表示される最大の画像、もしくはテキストブロックのレンダリング時間</b>の指標であり、<b>Core Web Vitalsのひとつ</b>です。

### LCPで考慮される要素
- `<img>` 要素
- `<svg>`要素内の`<image>`要素
- `<video>`要素（poster属性を使用）
    - poster属性：ドキュメントへ埋め込む動画ファイルの代わりに表示する画像ファイルのURLを指定する属性
- `url()`で読み込まれた背景画像を持つ要素
- テキストノードやその他のインラインレベルのテキスト要素の子を含むブロックレベルの要素

※[将来的に`<svg>`や`<video>`などの要素が追加される可能性があります。](https://web.dev/lcp/)

### "最大の要素"の決定方法
- Largest Contentful Paintで鍵となる「要素のサイズ」は、ビューポート内でユーザーに見えるサイズを指す

    - つまり、要素がビューポートの外に出ている場合や要素の一部が見えない場合、それらの部分は要素のサイズとしてカウントされません。

- 表示されている画像要素のサイズが本来のサイズから変更されている場合、Largest Contentful Paintではより小さい方が考慮される

    - 実際のサイズより小さいサイズに縮小された画像は表示されているサイズが、実際のサイズより大きなサイズに拡大された画像は実際のサイズが考慮されます。

- テキスト要素は、そのテキストノードを包含する最小の矩形のサイズが考慮される

- すべての要素について、CSS によって適用されたマージン・パディング・ボーダーは考慮されない

- 要素のサイズや位置を変更しても新しいLCP候補は生成されない（ビューポート内のエレメントの初期サイズと位置のみが考慮される）

- 最初は画面外でレンダリングされその後ビューポート内に遷移する画像は、考慮されない場合がある

- 最初はビューポート内にレンダリングされた要素が押し下げられてビュー外に出た場合、初期のビューポート内でのサイズが考慮される

    - 一方で、要素が DOM から削除されたり、関連する画像リソースが変更されたりした場合は、候補から除外されます。

### 測定方法
フィールドデータもしくはラボデータを用いて測定されます。

## TTI: Time to Interactive
参考
https://web.dev/tti/
https://web.dev/interactive/

TTIは<b>ページが完全にインタラクティブになるまでの時間（ページの読み込みが開始されてから、メインのサブリソースが読み込まれるまでの時間）</b>の指標です。

コンテンツの可視性のみを最適化してインタラクティブ性を犠牲にしてしまうと、「ページの読み込みが完了したように見える一方で、リンクやボタンを操作しようとしても何も起こらない」のようにイライラするユーザー体験を生み出してしまう可能性があることから、TTIは重要な指標です。

### TTIの計算方法
 <img width="700" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/6b52916d-1e11-6516-5237-1abd5db2a5fe.png">

1. First Contentful Paint(FCP)からスタート
2. Long task(50ms以上の処理)がなく、ネットワーク接続が2本以下の状態が5秒以上続いている期間を探す
3. 2から後方を検索し、一番近いLong taskを探す（Long taskが見つからない場合はFCPで停止）
4. TTIは、3で見つけたLong taskの終了時間（Long taskが見つからない場合はFCPと同じ値）

### FCPとTTIに差があると...
SSR（サーバーサイドレンダリング）のような技術は「一見インタラクティブなページに見えるが、実際はメインスレッドがブロックされているか、それらの要素を制御するJavaScriptの読み込みが完了していないためインタラクティブではない」のような状態を生み出す可能性があります。

このようなページとインテラクトしようとした場合、ユーザーはページの反応が遅いことにイライラするか、最悪の場合ページが壊れていると判断・離脱し、ブランドに対する信頼や信頼が損なわれてしまう可能性があります。

この問題を避けるために、FCPとTTIの差を最小限に抑えるように努力する必要があります。

※もしくは、両者に顕著な差がある場合には、ページのコンポーネントがまだインタラクティブではないことを視覚的な指標で明らかにする（ローディングアニメーションを使用するなど）必要があります。

### TTIの測定方法
ラボデータで測定されます。
※フィールドデータでの測定も可能ですが、ユーザーのインタラクションがページの TTI に影響を与える可能性があるため推奨されていません。

## TBT: Total Blocking Time
参考
https://web.dev/tbt/
https://web.dev/lighthouse-total-blocking-time/

TBTは<b>マウスクリックや画面タップ、キーボードの押下などのユーザー入力にページが反応しないようにブロックされている時間の合計</b>を表す指標です。

- 合計: First Contentful Paint から Time to Interactive までの間のすべてのLong task（50ms 以上実行されるタスク）のブロッキング部分を加算
- ブロッキング部分: 50ms以降の時間。たとえばLighthouseが70msのLong taskを検出した場合、ブロッキング部分は20ms


例）
下図のような5つのタイムラインがあるとします。このうち、Longtaskは実行時間が50msを超えている3つのタスクです。

 <img width="700" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/7c41fbe6-3782-b3a9-bdc6-52efe6bcc039.png">

このうちブロッキング部分はLongtaskの50ms以降の時間なので、下図のようになります。

 <img width="700" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/a28efdf5-5927-d1e1-052f-689e7416b807.png">

TBTはこのブロッキング部分の合計です。

### TBTの測定方法
ラボデータで測定されます。
※フィールドデータでの測定も可能ですが、TTIと同様ユーザーのインタラクションがページの TBTに影響を与える可能性があるため推奨されていません。

## Cumulative Layout Shift
参考
https://web.dev/cls/


CLSは<b>ページの全ライフサイクルの間に発生する予期せぬレイアウトシフトの合計</b>を表す指標であり、<b>Core Web Vitalsのひとつ</b>です。

- レイアウトシフト: ビューポート内に表示されている要素の開始位置が 2 つのフレーム間で変更されること。
既存の要素の開始位置が変更された場合にのみ発生します（新しい要素が DOM に追加されたり、既存の要素のサイズが変更されたりしてもレイアウトシフトとしてはカウントされません）。

### スコアの算出方法
```
layout shift score = impact fraction * distance fraction
```

#### impact fraction
ビューポートの総面積に対するすべての不安定要素の前のフレームと現在のフレームの可視領域の和の割合


<img width="400" title='image.png' alt='image' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/89a5e98f-2b3c-2fc2-582c-ce4a257a20c1.png'>

上画像中の要素を例にすると

1. あるフレームでビューポートの半分を占めている
2. 次のフレームではビューポートの高さの25%分だけ下に移動している
3. 両フレームでの要素の可視領域の和は赤い点t線の四角形に相当し、これはビューポート全体の75%なので、インパクト率は0.75

#### distance fraction
不安定要素がフレーム内を移動した最大の距離（水平or垂直）をビューポートの最大寸法（幅or高さのいずれか大きい方）で割ったもの

<img width="400" title='image.png' alt='image' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506732/d38e2a1a-4d08-894f-d447-d59e2c5c53f4.png'>

上画像中の要素を例にすると

- 最大のビューポート寸法は高さ
- 不安定要素はビューポートの高さの25%移動している
- distance fractionは0.25

#### layout shift score
```
layout shift score = impact fraction * distance fraction = 0.75 * 0.25 = 0.1875
```

### 測定方法
ラボデータとフィールドデータで測定されます。

※ラボツールは通常合成環境でページをロードする都合上ページのロード中に発生するレイアウトシフトしか測定できないため、ラボデータで測定するCLS 値はフィールドツールで測定したものより低い可能性があります。

## FID: First Input Delay
参考
https://web.dev/fid/

FIDは<b>リンクのクリックやボタンのタップなどユーザーが最初にページとインテラクトしたタイミングから、ブラウザが実際に応答してイベントハンドラの処理を開始できるようになるまでの時間</b>の指標であり、<b>Core Web Vitalsのひとつ</b>です。

最初の入力を考慮すべき理由としては、FIDはサイトの応答性に対するユーザーの第一印象となり、第一印象はサイトの品質と信頼性に対する全体的な印象を形成する上で非常に重要であることが挙げられています。

#### "最初の入力"として何をカウントするか
FIDはロード中のページの応答性を測定する指標であることから、<b>クリック</b>、<b>タップ</b>、<b>キー入力</b>などの離散的なアクションからの入力イベントのみに焦点を当てています。

一方で、スクロールやズームのような連続的なアクションを持つインタラクションは全く異なるパフォーマンスの制約があることから、FIDを測定する際の対象とはされていません。

#### 測定方法
フィールドで測定されます。FIDは実際のユーザーがインテラクションする必要があるため、ラボでは測定できません。

ただしTotal Blocking Time (TBT) メトリックはラボで測定可能で、これはFIDと相関関係にあり、インタラクティブ性に影響を与える問題も把握できるので、ラボでTBTを改善する最適化は、フィールドのFIDを改善することにつながります。

## おわりに
冒頭でも述べた通り、今後はより多くのWebサイトがWebパフォーマンスを意識する必要が出てくることが予想されます。

私自身も現在は各Webパフォーマンスツール・評価指標の調査や情報のキャッチアップに留まっていますが、今後は改善施策の提案・実行をチームとしてチャレンジしていこうと考えています。