パフォーマンス
パフォーマンスのファセット
パフォーマンスを 3 つのファセットに分類します。
バックエンドパフォーマンス
バックエンドパフォーマンスは、API、コントローラー、コマンドラインインターフェース(例: git)のレスポンスタイムにスコープされます。
DRI: Tim Zallman、VP of Engineering, Core Development.
パフォーマンス指標:
フロントエンドパフォーマンス
フロントエンドパフォーマンスは、GitLab の可視ページと UI コンポーネントのレスポンスタイムにスコープされます。
DRI: Tim Zallman、VP of Engineering, Core Development
パフォーマンス指標:
インフラストラクチャパフォーマンス
インフラストラクチャパフォーマンスは、GitLab SaaS インフラストラクチャのパフォーマンスにスコープされます。
DRI: Marin Jankovski、Sr. Director of Infrastructure, SaaS Platforms.
パフォーマンス指標:
その他の関連ページ
- GitLab.com (インフラ) アーキテクチャ
- GitLab.com のモニタリング
- アプリケーションアーキテクチャドキュメント
- GitLab.com の設定
- GitLab パフォーマンスモニタリングドキュメント
- パフォーマンステストツール
ここに記載されているさまざまな Issue を追跡するメタ Issue はインフラストラクチャトラッカーにあります。
GitLab のアプリケーションパフォーマンス
測定
目標
GitLab および GitLab.com のパフォーマンスは、最終的にはユーザー体験に関するものです。プロダクト管理ハンドブックにも説明されているように、「より高速なアプリケーションはより優れたアプリケーション」です。
現在の私たちの焦点は 2 つの指標です:
- Largest Contentful Paint (LCP):完全な読み込みパフォーマンスを測定するもの。良好なユーザー体験を提供するために、ページが最初に読み込み始めてから 2.5 秒以内に LCP が発生する必要があります。
- Time to First Byte (TTFB):バックエンドがベースページを送信するのにどれだけ時間がかかるかを把握するもの。良好なバックエンドレンダリングの目標は 500ms 未満です。
中期的には、First Input Delay (FID) と Cumulative Layout Shift (CLS) にもより大きな焦点を当てながら、すべての Web Vitals に注目することを目標としています。そのため、メイン指標でルートが既に良好に動作している場合は、それらの最適化を拡張してください。
グループはパフォーマンスに関するユーザー体験を密接にモニタリングし、測定されたパフォーマンス指標以外でも知覚パフォーマンスを改善する必要があります。例えば、読み込み後の操作が非常に遅く時間がかかる場合などです。
測定対象
sitespeed.io を通じて 4 時間ごとに自動実行することで、可能なすべてのエンドユーザーパフォーマンスメトリクスを測定します。収集したデータはすべて、特定のルートの改善やボトルネックの分析に役立てられます。すべての Grafana ダッシュボードに使用される継続的なデータストレージとして graphite インスタンスにデータを送信しています。さらに、より詳細なインサイトデータ、スローモーションデータ、HAR ファイル、完全な Lighthouse レポートを持つ完全レポート(sitespeed ダッシュボードの Runs トグルを有効にするとリンクが表示されます)も保存しています。
測定方法
現在、空のキャッシュ、Cable に制限した接続、us-central にある中程度の CPU ベースのマシンで 4 時間ごとに測定しています。
過去と現在のパフォーマンス
以下のテーブルに示す GitLab.com の URL は、パフォーマンス改善を測定するための基準を形成しています。これらは頻繁に使用されるケースです。時間はウェブリクエストから「ページの可視部分が表示される平均時間」(Speed Index の定義による)までの経過時間を示しています。これらの URL の「ユーザー」はこの場合制御されたエンティティであるため、以前のパフォーマンス指標「Speed Index」の_外部_測定値を表しています。
| タイプ | 2018-04 | 2019-09 | 2020-02 | 現在* |
|---|---|---|---|---|
| Issue リスト: GitLab FOSS Issue リスト | 2872 | 1197 | - | N/A |
| Issue リスト: GitLab Issue リスト | 1581 | |||
| Issue: GitLab FOSS #4058 | 2414 | 1332 | 1954 | |
| Issue ボード: GitLab FOSS リポジトリボード | 3295 | 1773 | - | N/A |
| Issue ボード: GitLab リポジトリボード | 2619 | |||
| マージリクエスト: GitLab FOSS !9546 | 27644 | 2450 | 1937 | |
| パイプライン: GitLab FOSS パイプライン | 1965 | 4098 | - | N/A |
| パイプライン: GitLab パイプライン | 4289 | |||
| パイプライン: GitLab FOSS パイプライン 9360254 | 4131 | 2672 | 2546 | |
| プロジェクト: GitLab FOSS プロジェクト | 3909 | 1863 | - | N/A |
| プロジェクト: GitLab プロジェクト | 1533 | |||
| リポジトリ: GitLab FOSS リポジトリ | 3149 | 1571 | - | N/A |
| リポジトリ: GitLab リポジトリ | 1867 | |||
| 単一ファイル: GitLab FOSS 単一ファイルリポジトリ | 2000 | 1292 | - | N/A |
| 単一ファイル: GitLab 単一ファイルリポジトリ | 2012 | |||
| Explore: GitLab explore | 2346 | 1354 | 1336 | |
| スニペット: GitLab スニペット 1662597 | 1681 | 1082 | 1378 |
*sitespeed grafana ダッシュボードにアクセスするには、Google アカウントにログインする必要があります。
注: このテーブルは単一コードベース化の前後にまたがるため、まったく同じプロジェクトではないにもかかわらず、比較を可能にするために GitLab FOSS ページを GitLab のページに近い位置に配置しています。
すべての Sitespeed ダッシュボード
runs トグルを有効にすると、すべての完全レポートへのリンクを持つアノテーションが表示されます。現在は 2 時間ごとに測定を実行しています。
ステップ
ウェブリクエスト
速度計()シンボルで始まるすべての項目は、_測定している_フロー内のステップを表しています。速度計アイコンは可能な限りモニタリングの関連ダッシュボードにリンクされています。以下のリストの各ステップは、目標テーブルの対応するエントリにリンクされています。
ユーザーがブラウザーを開き、gitlab.com/dashboard と入力してダッシュボードにアクセスするシナリオを考えてみてください。以下がその流れです:
- ユーザーリクエスト
- ユーザーがブラウザーに gitlab.com/dashboard と入力して Enter を押す
- DNS での IP 検索(非測定)
- ブラウザーが DNS サーバーで IP アドレスを検索
- DNS リクエストが送受信される(通常 ~10-20 ms [データ?];多くの場合すでにキャッシュされているのでより早い)
- ブラウザーからアプリケーションまでのステップの詳細については、https://github.com/alex/what-happens-when をご覧ください。
- ブラウザーから Azure LB(非測定)
- IP アドレスを見つけたブラウザーは、ウェブリクエスト(gitlab.com/dashboard 用)を Azure のロードバランサー(LB)に送信する
- バックエンドプロセス
- Azure LB から HAProxy(非測定)
- Azure のロードバランサーがパケット(リクエスト)のルーティング先を決定し、フロントエンドロードバランサー(HAProxy とも呼ばれる)にリクエストを送信する
- HAProxy によるブラウザーとの SSL(非測定)
- HAProxy(ロードバランサー)がブラウザーと SSL ネゴシエーションを行う
- HAProxy から NGINX(非測定)
- HAProxy がフロントエンドワーカーの NGINX にリクエストを転送する。この場合、ウェブリクエストを追跡しているため、本番アーキテクチャ図の「Web」ボックス内の NGINX ボックスになる。ただし、コマンドラインからの API や git コマンドを経由してリクエストが来ることもあるため、その図の API および git の「ボックス」も存在する。
- すべてのサーバーが 1 つの Azure VNET にあるため、HAProxy と NGINX 間の SSL ハンドシェイクとティアダウンのオーバーヘッドはほぼ無視できるほど小さい。
- NGINX によるリクエストバッファリング(非測定)
- NGINX がリクエストに関連するすべてのネットワークパケットを収集する(「リクエストバッファリング」)。リクエストは中間ネットワークによって複数のパケットに分割される場合がある。詳細については MTU を参照。
- 他のフローでは、これは当てはまらない。具体的には、LFS ではリクエストバッファリングが無効化されている。
- NGINX から Workhorse(非測定)
- NGINX が完全なリクエストを Workhorse に転送する(1 つの結合リクエストとして)。
- Workhorse によるリクエスト配信
- Unicorn によるサービス呼び出し
- Unicorn(しばしば「Rails」または「アプリケーションサーバー」と呼ばれる)は、リクエストを Rails コントローラーリクエストに変換する。この場合は
RootController#index。リクエストが Unicorn で_開始_し Unicorn を_離れる_までのラウンドトリップタイムをTransaction Timingsと呼ぶ。RailsController リクエストは以下に送信される(そこからデータが受信される): - PostgreSQL(
SQL timings) - NFS(
git timings) - Redis(
cache timings) - この
gitlab.com/dashboardの例では、コントローラーはすべての 3 つを処理する 。 - 通常、特定のコントローラーリクエストに対して_複数の_ SQL 呼び出し(またはファイル、キャッシュなど)があります。これらは合計タイミングに加算され、特に順次実行されるため重要です。たとえば、このシナリオでは、上記の_特定のユーザー_が
gitlab.com/dashboard/issuesにアクセスする際に 29 の SQL 呼び出しが発生します(Loadで検索)。SQL 呼び出しの数は、そのユーザーが持つプロジェクトの数や、すでにキャッシュされている量などによって異なります。 - Rails はコントローラーリクエスト内のステップを順次処理する。つまり、データベースと git への呼び出しが必要な場合、それらを並列に実行するのではなく、最初のステップへの応答を待ってから次のステップに進む。
- Rails スタックでは、ミドルウェアは通常、コントローラー呼び出しごとに Redis、NFS、PostgreSQL へのラウンドトリップ数を増やす。ミドルウェアは {セッション状態、ユーザー ID、エンドポイント認可、レート制限、ログ記録など} に使用され、コントローラーは通常、{設定の取得、キャッシュチェック、モデルビューの構築、キャッシュ保存など} のそれぞれに対して少なくとも 1 回のラウンドトリップを持ちます。各ラウンドトリップは < 10 ms と_推定_されます。
- Unicorn(しばしば「Rails」または「アプリケーションサーバー」と呼ばれる)は、リクエストを Rails コントローラーリクエストに変換する。この場合は
- Unicorn によるビューの構築
- ビューの構築には長い時間がかかることがある(
view timings)。一部のコントローラーでは、最初にデータが収集され、その後ビューが構築される。他のコントローラーでは、ビュー_内から_データが収集されるため、それらの場合のview timingには NFS、PostgreSQL、Redis などの呼び出しにかかった時間が含まれる。多くの場合、両方が行われる。 - Rails の特定のビューは、多くの場合、複数の部分ビューから構築される。これらはコントローラーアクションで指定されたテンプレートファイルから使用され、テンプレートファイル自体は一般的にレイアウトテンプレート内に含まれる。パーシャルは他のパーシャルを含めることができる。これは適切なコードの整理と再利用のために行われる。例として、上記の_特定のユーザー_が
gitlab.com/dashboard/issuesを読み込む際には、56 個のネストされた/部分ビューがレンダリングされる(View::で検索)。 - 部分ビューは、フラグメントキャッシングなど、さまざまな Rails のテクニックを通じてキャッシュできる。さらに、GitLab には Markdown から HTML への変換を高速化するためにデータベースに保存された Markdown キャッシュがある。
- First Paint という意味での知覚パフォーマンスは、バックエンドによってレンダリングされるビューのコンテンツ量と、ユーザーに「最小限の」HTML ブロブを送信して Javascript / AJAX などに依存してページを First Paint から「完全読み込み」に移行させる追加要素の取得を比較することで影響を受ける。これについての詳細はフロントエンドのセクションを参照。
- ビューの構築には長い時間がかかることがある(
- Unicorn による HTML 生成(非測定)
- ビューが構築されると、Unicorn はブラウザーに返される「HTML ブロブ」の作成を完了する。
- これらのブロブの一部は計算コストが高く、レンダリング後に Unicorn から Redis に送信(つまりキャッシュ)するようにハードコードされることがある。
- HTML からブラウザーへ(非測定)
- HTML ブロブは次のパスを経てブラウザーに送信される:
- Unicorn から Workhorse(非測定)
- Workhorse から NGINX(非測定)
- NGINX から HAProxy(非測定)
- HAProxy から Azure LB(非測定)
- Azure LB からブラウザーへ(非測定)
- Azure LB から HAProxy(非測定)
- ページレンダリング
- ブラウザーが最初のバイトを受信する時間。バックエンドのすべてに加えて、ネットワーク速度にも依存する。上記の速度計にリンクされているダッシュボードでは、First Byte は US にある Digital Ocean ボックスからネットワーク遅延が比較的少ない状態で測定されており、内部 First Byte の推定値を表す。first byte の過去のパフォーマンスはこのページの別の場所に記録されている。
- すべてのページに対して、ブラウザーの「inspect」ツールを使用して「TTFB」(time to first byte)を確認できる。
-
First Byte - Externalは SiteSpeed を使用して手動で選択した URL について測定されている。
- ブラウザーが HTML ブロブを解析し、GitLab.com に対して javascript バンドル、CSS、画像、ウェブフォントなどのアセットを取得するための追加リクエストを送信する。
- このステップのタイミングは(他の要素の中でも特に)アセットの数と大きさ、およびネットワーク速度に依存する。_各_静的アセットについて、ラウンドトリップが発生する: - キャッシュされたアセットの場合: ブラウザー nginx nginx がキャッシュされたアセットがまだ有効であることを確認 ブラウザー - キャッシュされていないまたは期限切れのキャッシュアセットの場合: ブラウザー workhorse workhorse がローカルキャッシュからアセットを取得 ブラウザー - GitLab Pages を通じて提供されるページの場合: ブラウザー pages デーモン (アーキテクチャ内の独立したサービス) ブラウザー
- スタイルシートはデフォルトでページレンダリングをブロックする場合があり、ページレンダリングに不必要な遅延をもたらす可能性がある。
- 9.5 以降、スクリプトは
defer="true"で読み込まれるためレンダリングをブロックしなくなり、HTML + CSS がレンダリングされた後にのみ呼び出された順序で解析・実行される。 - 「Speed Index」を計算するのに十分な意味のあるコンテンツが画面にレンダリングされる。
- スクリプトが読み込まれると、Javascript がページ内でそれらをコンパイルして評価する。
- 一部のページでは、非同期読み込みを可能にするために AJAX を使用している。AJAX 呼び出しはフロントエンド要素(ボタン)や
DOMContentLoadedイベントなど、あらゆる種類のものによってトリガーされる。新しい呼び出しは新しい URL 用であり、そのようなリクエストはバックエンドの Web または API ワーカーを経由してルーティングされ、それぞれの Rails コントローラーを呼び出し、リクエストされたファイル(HTML、JSON など)を返す。例えば、gitlab.com/usernameというページのカレンダーとアクティビティフィードはDOMContentLoadedによってトリガーされる 2 つの別々の AJAX 呼び出しです。(DOMContentLoadedイベントは「DOM の準備ができており、JavaScript の実行をブロックするスタイルシートがない 時点 を示す」(クリティカルレンダリングパスについての記事から引用))。AJAX を使用する代わりに、gitlab.com/usernameURL によって呼び出される同じコントローラー内でカレンダーとアクティビティフィードを生成する完全な Rails コードを含めることもできるが、これは単にデータベースへの呼び出しが増えるなどにより First Paint が遅くなる結果をもたらす。
Git コミットプッシュ
まず上記のウェブリクエストのステップを読み、その後ここから続けてください。
例えば ウェブ UI から、リポジトリにプッシュした後:
- ウェブブラウザーでリポジトリファイルを編集し、コミットメッセージを入力して「Commit」をクリックする
- NGINX が git コミットを受信し、Workhorse に渡す
- Workhorse が
git-receive-packプロセス(workhorse マシン上)を起動して新しいコミットを NFS に保存する - workhorse マシン上で、
git-receive-packが git フックを起動してGitLab Shellをトリガーする- GitLab Shell は SSH 経由でプッシュされた Git ペイロードを受け入れ、それに基づいて(例: プッシュを実行する権限があるかどうかを確認したり、データを処理のためにスケジューリングしたりする)
- この場合、GitLab Shell は
post-receiveフックを提供し、git-receive-packプロセスがリポジトリにプッシュされた内容の詳細をpost-receiveフックに渡す。具体的には、古いリビジョン、新しいリビジョン、ref(タグまたはブランチ)名の 3 つの項目のリストを渡す。
- Workhorse は
post-receiveフックを Sidekiq キューである Redis に渡す- Workhorse はプッシュが成功したか失敗したかを通知される(リポジトリが利用できない、Redis がダウンしているなどの理由で失敗する可能性がある)
- Sidekiq は Redis からジョブを取り上げ、キューからジョブを削除する
- Sidekiq は PostgreSQL を更新する
- Unicorn は PostgreSQL にクエリを実行できるようになる
目標
ウェブリクエスト
ユーザーがブラウザーを開き、GitLab.com のお気に入り URL を表示するシナリオを考えてみてください。ステップは「ウェブリクエスト」のセクションに説明があります。このテーブルでは、ステップが測定され、改善の目標が設定されています。
このテーブルのガイド:
- すべての時間はミリ秒で報告されます。
# per request:リクエストごとにこのステップが発生する平均回数。例えば、平均的な「トランザクション」には 0.2 の SQL 呼び出し、0.4 の git 呼び出し、1 回のキャッシュ呼び出し、および 30 個のネストされたビューの構築が必要かもしれません。p99 Q2-17:Q2 2017 末の p99 タイミング(ミリ秒)p99 Now:現在の p99 タイミングを表示するダッシュボードへのリンクp99 Q3-17:Q3 2017 末までの p99 タイミングの目標- 斜体 の数字は イベントごと または他のタイミングと 並列 であり、(小計に合算されない)。斜体でない数字は(小計に)合算されます。
注:
Git コミットプッシュ
テーブルを作成予定。マージリクエスト歓迎!
修飾語
すべてのパフォーマンスメトリクスに対して、以下の修飾語を適用できます:
- ユーザー: 実際の GitLab ユーザーが時間を体験・測定する方法。
- 内部: GitLab.com のインフラストラクチャ_内から_測定された時間(境界は「ネットワーク | Azure ロードバランサー」インターフェースとして定義)。
- 外部: GitLab.com のインフラストラクチャ外の指定された任意の地点から測定された時間。例えば、Prometheus モニタリングを持つ DO ボックス、または指定されたネットワーク速度の指定された地域のブラウザーなど。
First Byte
外部
First Byte のタイミング履歴を以下のテーブルに示します(_現在の_タイミングについては速度計アイコンをクリックしてください)。すべての時間はミリ秒です。
| タイプ | Q4-17 末 | 現在 | ||
|---|---|---|---|---|
| Issue: GitLab CE #4058 | 857 | |||
| マージリクエスト: GitLab CE !9546 | 18673 | |||
| パイプライン: [GitLab CE パイプライン 9360254] | 1529 | |||
| リポジトリ: GitLab CE リポジトリ | 1076 |
内部
アプリケーションとインフラストラクチャのパフォーマンスをフロントエンドとネットワークの側面を考慮せずにより深く測定するために、Unicorn によって記録された「トランザクションタイミング」を確認します。これらのタイミングはアクセスされる URL ごとに Rails コントローラーダッシュボードで確認できます。
可用性とパフォーマンスのラベル
可用性
このセクションは可用性の重大度に移動されました。
パフォーマンス
GitLab.com のパフォーマンスに関連する Issue の優先度を明確にするために、~performance ラベルと「Severity」ラベルを追加する必要があります。どの重大度ラベルを選択するかは、以下の 2 つの要因が影響します:
- 何かがどれだけ頻繁に使用されるか。
- 何かが障害を引き起こす可能性がどれだけ高いか。
厳密にパフォーマンスに関連する作業については、Controller Timings Overview Grafana ダッシュボードを使用できます。このダッシュボードは、データを 3 つのカテゴリに分類しており、それぞれに関連する重大度ラベルがあります:
- 頻繁に使用される:
~severity::2 - 一般的に使用される:
~severity::3 - まれに使用される:
~severity::4
つまり、コントローラー(例: UsersController#show)が「頻繁に使用される」カテゴリにある場合は、~severity::2 ラベルを割り当てます。
データベース関連のタイミングには、SQL Timings Overview も使用できます。これはデータベースチームがデータベース関連のパフォーマンス作業に使用する AP ラベルを決定するために主に使用するダッシュボードです。
データベースパフォーマンス
非常に大まかなレベルで、データベースパフォーマンスに影響するパラメーターに関するいくつかの一般的な注意事項です。
- ホワイトボックスモニタリングから:
- Rails コントローラーで費やされる時間のうち、データベースに費やされる割合: https://dashboards.gitlab.net/d/web-rails-controller/web-rails-controller?viewPanel=13864&orgId=1(特定の Rails コントローラー/ページについて)
- グローバル SQL タイミング: https://dashboards.gitlab.net/dashboard/db/transaction-overview?panelId=9&fullscreen&orgId=1&from=now-2d&to=now
- 単一の HTTP リクエストは単一のコントローラーを実行します。コントローラーは通常、利用可能なデータベース接続を 1 つだけ使用しますが、最初に読み取りを実行し、その後に書き込みを実行する場合は 2 つ使用することがあります。
- pgbouncer は最大 150 の同時 PostgreSQL 接続を許可します。この制限に達した場合、PostgreSQL 接続が利用可能になるまで pgbouncer 接続をブロックします。
- PostgreSQL は最大 300 の接続(アクティブかどうかにかかわらず、接続されているもの)を許可します。この制限に達すると、新しい接続は拒否され、アプリケーションでエラーが発生します。
- プロセス数がデータベースサーバーで利用可能なコア数を超えると、CPU はリクエストされたプロセスを実行するために常にコアを切り替えます。このコアの競合によりパフォーマンスが低下する可能性があります。
- データベースの CPU 負荷が 100% 未満(https://dashboards.gitlab.net/dashboard/db/postgresql-overview?refresh=5m&orgId=1&from=now%2Fw&to=now&panelId=13&fullscreen)である限り、理論的にはレイテンシを追加せずにより多くの負荷を処理できます。実際には、データベースの専門家は CPU 負荷を 50% 未満に保つことを好みます。
- 負荷が根本的なアプリケーション設計によってどのように決まるかの例として: DB CPU 使用率は以前は低かった(20%、9.2 以前)が、9.2 RC1 が公開されたときに 50-75% に上昇し、9.2 がリリースされるまでに 20% に戻った。
- pgbouncer
- 機能: pgbouncer は N の受信接続を M の PostreSQL 接続にマッピングし、N >= M となります(N < M では意味がない)。例えば、1024 の受信接続を 10 の PostgreSQL 接続にマッピングできます。これは主に同時に処理したいクエリの数によって影響されます。例えば、GitLab.com では、プライマリが 100 を超えることはほとんどなく(通常は 20-30 前後)、セカンダリが 20-30 の同時クエリを超えることもほとんどありません。セカンダリを追加するほど、負荷を分散できるため必要な接続数が減ります(サーバーが多くなるコストと引き換えに)。
- アナロジー: pgbouncer は多くのお客様に飲み物を提供するバーテンダーです。彼女は自分で飲み物を作る代わりに、20 人の「バックエンド」バーテンダーのうちの 1 人にそれを指示します。バーテンダーの 1 人が飲み物を作っている間、他の 19 人(「メイン」バーテンダーを含む)は新しい注文に対応できます。飲み物が完成すると、20 人の「バックエンド」バーテンダーのうちの 1 人がメインバーテンダーに渡し、メインバーテンダーがそれを注文したお客様に渡します。このアナロジーでは、N の受信接続がバーのお客様であり、M の「バックエンド」バーテンダーがいます。
- pgbouncer のフロントエンド接続(受信接続)は非常に安価で、多数(例: 数千)持つことができます。通常、N >= A となるようにしたい(N が pgbouncer の接続制限、A がアプリケーションに必要な接続数)。
- PostgreSQL 接続はリソースの観点からはるかに高コストで、理想的にはサーバーごとに利用可能な CPU コア数以下にする(例: 32)。負荷によっては常にこれで十分とは限らず、例えばプライマリはピーク時に 100-150 の接続を許可する必要があります。
- pgbouncer は、一定期間アイドル状態になった PostgreSQL 接続を終了するように設定でき、リソースを節約できます。
1. この範囲は、First Byte テーブルに提供されている 4 つのサンプル URL の First Byte タイムの範囲に対応しています。ただし、このダッシュボードで測定されたすべての 非ステージング URL に基づくと、2017-03-30 から 2017-06-28 の間では、数値は 3,833 ms になります。 ↩︎
2. この範囲は、Speed Index テーブルに提供されている 4 つのサンプル URL の Speed Index の範囲に対応しています。 ↩︎
3. この範囲は、Speed Index テーブルに提供されている 4 つのサンプル URL の完全読み込みタイムの範囲に対応しています。 ↩︎
