Web クライアントサイドのパフォーマンスメトリクス — Speed Index、Paint Timing、TTI etc...

色々なパフォーマンス指標のこと

何かを評価するときには何らかの指標(メトリクス)を定めますが、何を指標として設定してどのように測るかというのは重要です。

いわゆる KPI もそうですが、扱っている商材やビジネスのステージ(フェーズ)によっても適切な指標は変わるかもしれません。色々な指標をよく理解して引き出しを広げておくことは、状況に合わせて適切な指標を選んで改善していく過程で役立ちます。

これまでのパフォーマンス指標

過去の Web パフォーマンス界隈はバックエンドから HTML ドキュメントを返却する際の所要時間や、Web ページロード時の各イベントの発火時間を計測する方法が多く見られました。

  • Backend Time
  • Browser Event Based
    • DOMContentLoaded
    • Page load ( onload )

近年は特に後者の、既定のイベント発火に依存していたクライアントサイドのパフォーマンス計測に関する指標が見直されてきています。見直される理由としては DOMContentLoaded はいわずもがな load であっても、ユーザー体験上コンテンツが届いていることを保証しないことが挙げられます。これらのイベントは Web ページロード処理上の1ステップに過ぎないので、パフォーマンスの計測指標には本来なりえません。

ここからはカジュアルに計測できる指標の変遷を中心に説明します。

FP ( First Paint ) — ページが表示され始めたとき

そこでユーザー体験と直結しうるビジュアルに基づいた指標のひとつとして、First Paint が生まれます。「ナビゲーションの開始(白い画面の状態)から、Web ページの何らかが表示され始めたタイミング」を指します。

FP を改善するには CRP ( クリティカルレンダリングパス ) の短縮が有効です。

First Paint は非標準な API ではありますが、Chrome であれば chrome.loadTimes().firstPaintTime で、Edge や Internet Explorer であれば window.performance.timing.msFirstPaint で取得できます。API から取得できない場合はキャプチャによる画像比較のアプローチが考えられます。

Speed Index — ページ表示の速度を示すスコア

Web ページに非同期処理やブロッキングが多く潜む昨今、First Paint だけでは、ユーザーにコンテンツに素早く表示されたかは分かりません。そこで、Web ページが表示される過程の進捗 ( Visual Progress ) とその速度を用いた Speed Index という指標が生まれます。大雑把にいうと「Web ページのコンテンツがどれくらい速く表示されたかを表すスコア」です。

Speed Index を改善するには CRP を短縮するだけでなく、FP 以後のコンテンツ表示を全体的に高速化する必要があります。

Speed Index の計算式や実際の算出方法など、詳しいドキュメントは webpagetest-doc-ja/index.md · t32k/webpagetest-doc-ja に和訳があります。また、ビデオキャプチャを必要とせず Resource Timing API から算出する実装は WPO-Foundation/RUM-SpeedIndex があります。


Visual Complete Progress

WebPagetest Documentation より


最近のパフォーマンス指標

前述の通りブラウザから直接取得できる指標が乏しい中、Speed Index という指標が作り出されてからはそれが重用されているわけですが、標準化も進んできてブラウザの API から取得できる指標も増えてきています。また、あらゆるケースを一般化して標準仕様にまとめることは困難ながら、新たな観点を提供する指標も生まれています。

(賢明な諸兄にはお察しの通り)以降の記述は次にあげるページの情報を参考に、適当にかいつまんだり付け足したりしています。


画面の表示進捗と FP, FCP, FMP, TTI の関係

Leveraging the Performance Metrics that Most Affect User Experience より


FCP ( First Contentful Paint ) — コンテンツが表示され始めたとき

FCP は WICG/paint-timing で定義される指標で「ナビゲーションの開始から、Web ページのコンテンツが表示され始めたタイミング」を指します。FP も同じ Paint Timing で改めて定義されています。

"first-contentful-paint" contain a DOMHighResTimestamp reporting the time when the browser first rendered any text, image (including background images), non-white canvas or SVG. This includes text with pending webfonts. This is the first time users could start consuming page content. WICG/paint-timing: A proposal for a Time To First Paint specification.

FCP と FP の違いは、FP はとにかく何らかビジュアル上の変化が最初に起きたとき成立しうるのに対して、FCP はコンテンツになりうるテキストや画像、SVG、Canvas 要素が最初に表示されたとき成立します。よくあるケースでは、ヘッダーやナビゲーションバーなどの表示が FCP の近似になりえます。

FCP は、そのうち標準化される(かもしれない) Paint Timing API から取得できるようになります。標準化されたパフォーマンス関係の指標は多くが PerformanceObserver(ほにゃらら) Timing API の組み合わせで成り立っています。直近だと Chrome 60 から次のようなコードが利用できます。

// https://developers.google.com/web/updates/2017/06/user-centric-performance-metrics#tracking_fpfcp
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // `name` will be either 'first-paint' or 'first-contentful-paint'.
    const metricName = entry.name;
    const time = Math.round(entry.startTime + entry.duration);

    ga('send', 'event', {
      eventCategory: 'Performance Metrics',
      eventAction: metricName,
      eventValue: time,
      nonInteraction: true,
    });
  }
});

// Start observing paint entries.
observer.observe({entryTypes: ['paint']});

FMP ( First Meaningful Paint ) — ユーザーに意味のある表示になったとき

FMP は急激にファジーな指標になりますが「Web ページがユーザーにとって、意味がある(役に立つ)表示になったタイミング」を指します。

FMP はあらゆるケースを一般化して標準仕様にまとめることは困難な指標に分類されます。例えばブログであれば記事のタイトルと本文が表示されたときでしょうし、検索エンジンであれば検索結果のリンクテキストが表示されたときであり、内容によって様々です。

FMP は User Timing and Custom Metrics で紹介されるような、特定のヒーローイメージやテキストなどが表示されたタイミングを任意に取得します。指標として Speed Index よりも直感的に分かりやすい反面、コンテンツ実装側で取得する工夫が必要になります。

Time to First Meaningful Paint: a layout-based approach には FMP に近似する値を多くのケースで導き出す評価アプローチの案がまとめられていますが、標準化の提案には至っていなさそうです。

TTI ( Time to Interactive ) — ユーザーの操作に応答できるようになったとき

TTI は「Web ページのロードが終わって、ユーザーの入力に対して確実に応答できるタイミング」を指します。Web ページの初期化に伴うリソースのロードや Long Tasks ( イベントループ上の 50ms 以上かかるタスク ) が一通り落ち着いて、RAIL モデル における Idle の要件を満たす状態です。

TTI の算出方法については GoogleChrome/tti-polyfill に Resource Timing API でネットワーク通信を、Long Tasks API で Long Task をそれぞれ監視してタイマー回しながら TTI を待ち受ける実装があります。

// https://developers.google.com/web/updates/2017/06/user-centric-performance-metrics#tracking_tti
import ttiPolyfill from './path/to/tti-polyfill.js';

ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => {
  ga('send', 'event', {
    eventCategory: 'Performance Metrics',
    eventAction: 'TTI',
    eventValue: tti,
    nonInteraction: true,
  });
});

First Interactive and First Consistently Interactive には TTI に関する定義と評価アプローチの案がまとめられていますが、これまた標準化の提案には至っていなさそうです。FMP の仕様化が難しい折、この文書で挙げられる定義自体も FMP に依存しているので道は険しそうです。

標準仕様予定の API になる前から polyfill とはこれいかに...

(要検討) 標準化されていないパフォーマンス指標の算出について

新興の標準化されていない指標をキャプチャなどに頼らずブラウザ内で完結して算出するソリューションは WPO-Foundation/RUM-SpeedIndex にせよ GoogleChrome/tti-polyfill にせよ、それなりに処理をがんばって値を算出しています。

例えば RUM-SpeedIndex が実行されている某サイトを眺めたところ、手元のマシンでは Speed Index の算出に 30-50 ms ほどかかっており、算出処理自体が Long Task 一歩手前という風情です。

Google Developers の記事でも RUM ( Real User Monitoring ) として GA にイベントを送信するサンプルコードがありますが、こういったソリューションに頼る場合は、算出処理自体のコストも見た上で RUM として常に実行するか Synthetic Monitoring 時に限定するかは検討したほうが良いかもしれません。

RUM-SpeedIndex は requestIdleCallback に呼び出しタイミングを預けるなどするとより安全なのかも

パフォーマンス予算

古いのかそうでもないのまで色々なパフォーマンス指標を紹介してきましたが、これらの指標を運用する包括的な考え方も重要です。

運用フェーズでよく出てくるのが Performance Budget、パフォーマンス予算という考え方です。例えばパフォーマンスを多角的な指標で定常計測するツールを使いつつ、各指標に設定された予算を上回ってしまったらアラートを飛ばすように設定するようなイメージです。放っておけばパフォーマンスは運用中に必ず低下していくので、それを避けて維持するための運用法です。

Performance Budget Metrics に書かれていますが、本記事で取り扱っていないものも含めたパフォーマンス指標はいくつかに分類することができます。

  1. Milestone timings
    • Examples: Load time; domContentLoaded; Time to render
  2. SpeedIndex
  3. Quantity based metrics
    • Examples: Total number of requests; Overall page weight; Total image weight
  4. Rule based metrics
    • Examples: PageSpeed score; YSlow score

今回は主に Milestone timings に類する指標を多く紹介しましたが、Quantity based metrics や Rule based metrics も予算管理上は有効です。

Rule base metrics は実際にユーザー体験をあらわす指標ではないという話もありますが、PageSpeed Insights や Lighthouse が提供するベストプラクティスを最低限満たせているかを常にチェックしておくことはプロダクトを運用していく上で有効です。テストカバレッジのような扱いのスコアと捉えても良いでしょう。

後記

以前からヒーローイメージなど各種コンテンツに基づくピンポイントな数字をとりたい人は User Timing and Custom Metrics で紹介されている中での performance.mark() が実行されているのと同様のタイミングで、 Date.now() して指標を GA ないし独自のデータストアに送信するなど工夫して指標を得ていたことでしょう。

それはそれとしつつ、近年は Speed Index や FP や FCP (もしかしたら将来的に FMP や TTI も...)のように、外部ツールからテストしてカジュアルに(コンテンツ側の特別な実装を必要とせずに)収集できる指標の発達がめざましいように感じます。

これらの指標が各ブラウザで取得できるようになるのはもちろん、SpeedCurve や Calibre のようなソリューションのダッシュボードに統合されていくこともこれから期待するところです。

とはいえ Calibre は FCP, FMP, TTI の項目もすでに一通りあるけど(使い物になるかはしらない)

ところで OGP 用のサムネイル画像は、先日調理した鶏手羽元のすっぱ煮(醤油40ml、みりん40ml、酢120ml、砂糖大さじ1)の画像です。おいしかったです。

参考

その他、本文中にリンクを貼らなかった参考 URL です。