公開日: 2024 年 3 月 6 日
データ圧縮は、対象となるページリソースのサイズを縮小する、実績のあるパフォーマンス最適化手法です。しばらくの間、ウェブサーバーで主に gzip を使用して、HTML、CSS、JavaScript ファイルなどの一般的なテキストベースのページリソースを圧縮し、クライアントに送信して解凍することが一般的でした。これにより、ページが意図したとおりに動作しながら、リソースの読み込み時間を短縮できます。
gzip はそれ自体が非常に効果的ですが、近年、ウェブでの圧縮がさらに改善されています。2016 年に、Chrome で Brotli アルゴリズムがリリースされ、対象となるリソースの圧縮率が全体的に向上しました。2017 年末までに、すべての最新ブラウザが Brotli をサポートし、サーバーでのサポートが普及し始めました。最近では、Chrome で ZStandard 圧縮がリリースされています。
しかし、作業はこれで終わりではありません。Chrome チームは、共有辞書をウェブで使用できるように取り組んでおり、現在、Brotli と ZStandard の両方でオリジン トライアルで利用できます。共有辞書は、Brotli と ZStandard の圧縮を補完して、更新されたコードを頻繁にリリースするウェブサイトの圧縮率を大幅に向上させることができます。場合によっては、90% 以上の圧縮率 を実現できます。この投稿では、共有辞書の仕組みと、オリジン トライアルに登録してウェブサイトの Brotli と ZStandard で使用する方法について詳しく説明します。こちらの動画もご覧ください:
共有辞書の概要
圧縮とは、入力内の冗長なシーケンスを見つけ、その情報を使用して、後で元に戻すことができるはるかに小さい出力を作成するプロセスです。圧縮は、リソースの読み込み時間を大幅に短縮するため、ウェブで効果を発揮します。Brotli と ZStandard はどちらも、圧縮中にこれらのアルゴリズムで使用できる追加のパターンを集めた圧縮辞書を使用することで、効果を高めることができます。実際、Brotli の高い効率は、内部辞書を使用することで実現されています。
ただし、特定のユーザーがキュレートしたカスタム辞書を Brotli と ZStandard で使用して、特定のリソースに固有のパターンを含めることができます。 実際には、カスタム辞書は外部ファイルであり、任意の入力に適用できます。辞書は、アプリケーションのプロダクション コードや、あらゆるコンテンツに特化したものにすることができます。特定の辞書が入力にどの程度適用できるかは、全体的な圧縮効率に大きな影響を与える可能性があります。入力の内容と非常によく似た辞書を使用すると、一般的な内容や類似性の低い内容の辞書よりも高い圧縮率の出力が得られます。
カスタム圧縮辞書の効果の例を次に示します。ウェブサイトで Angular フレームワークを使用しており、現在使用しているバージョンが 1.7.9 であるとします。このバージョンの Angular フレームワークは、圧縮されていない状態で約 172 KiB です。Brotli のデフォルト設定で圧縮すると、サイズは約 53 KiB になります。これにより、圧縮率はほぼ 70% になります。ただし、後で Angular 1.8.3 にアップグレードすることにしたとします。このバージョンの Angular はバージョン 1.7.9 とほぼ同じサイズであるため、前のバージョンとほぼ同じ圧縮率が期待できます。
ここで、カスタム辞書は、デルタ圧縮と呼ばれるプロセスを使用して役立ちます。これは、リソースの以前のバージョンの辞書を使用して、後のバージョンを圧縮する場合に使用します。 前の例を使用して、バージョン 1.7.9 を辞書として使用して Angular のバージョン 1.8.3 を圧縮した場合、出力は 4 KiB 強になります。これは、ほぼ 98% の圧縮率を表します。圧縮辞書は読み込みパフォーマンスに大きな影響を与える可能性があり、その効果は実際のアプリケーションで実現されています。
ただし、このフローをウェブで機能させるには課題があります。辞書を使用してリソースを圧縮する場合、それを解凍するには同じ辞書が必要です。 このフローは、ウェブ(SDCH)で以前に試みられましたが、安全に実装するのは困難でした。共有辞書圧縮に関するこの最新の提案では、これらの懸念に対処しながら、静的リソースと動的リソースの両方に大きなメリットをもたらしています。
Chrome が共有辞書のサポートをアドバタイズする方法
すべてのブラウザは、Accept-Encoding リクエスト ヘッダーを介してサポートする圧縮アルゴリズムをアドバタイズします。ヘッダーの内容は、サポートされているエンコードのカンマ区切りのリストです。
Accept-Encoding: gzip, br, zstd
この特定の Accept-Encoding ヘッダーは、リソースをリクエストするブラウザが gzip、Brotli、ZStandard の圧縮アルゴリズムをサポートしていることを示しています。リクエストに応答するウェブサーバーは、リクエストに応答する際に使用するアルゴリズムを決定できます。
共有辞書のサポートが有効になっていて、リソースに関連する辞書が利用可能な場合、追加のトークンが Accept-Encoding ヘッダーに追加されます。これらのトークンは、Brotli の場合は br-d、Zstandard の場合は zstd-d です。Chrome には、利用可能な辞書のハッシュも含まれます。これについては後述します。
Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
ウェブサーバーがこのトークンを認識するように構成されていて、辞書を認識できる場合、該当するエンコードの辞書を使用して圧縮されたリソースでそのリクエストに応答できます。実際にこれがどのように実現されるかは、リクエストが静的リソースと動的リソースのどちらに対するものかによって異なります。
静的リソースの共有辞書圧縮
静的ページリソースは、リクエストされた URL に対して常に同じレスポンスを生成するリソースです。圧縮可能な静的ページリソースの一般的な例としては、JavaScript ファイルと CSS ファイルがあります。これらのリソースは通常、キャッシュの目的で何らかの方法でバージョン管理されます。ファイル名にファイルの内容のハッシュ(styles.abcd1234.css など)を使用したり、リソースのフィンガープリントを取得したりします。これらのリソースタイプは、共有辞書が提供するデルタ圧縮の有力な候補です。静的リソースは長時間キャッシュされることが多く、頻繁に更新される傾向があるためです。
静的リソースの辞書を指定するには、そのリソースの Use-As-Dictionary レスポンス ヘッダーを設定します。ヘッダーはいくつかのキーと値のペアのいずれかを受け取りますが、必須のペアは match のみです。このペアは、URLPattern 構文を受け取ります。
Use-As-Dictionary: match="/dist/styles.*.css"
Use-As-Dictionary ヘッダーは、その中で指定されたパターンに一致するリソースの今後のバージョンに適用されるメカニズムと考えることができます。たとえば、ウェブサイトのすべてのスタイルを 1 つの CSS ファイルでリリースするとします。簡単にするため、そのリソースの最初のバージョンが /dist/styles.v1.css にあり、match 値が /dist/styles.*.css の Use-As-Dictionary レスポンス ヘッダーとともに送信されるとします。
しばらくすると、ウェブサイトの CSS を更新し、/dist/styles.v2.css にある新しいバージョンをリリースします。前のバージョンの Use-As-Dictionary レスポンス ヘッダーで使用されている match 値がこのリクエストに適用されるため、ブラウザは、構造化フィールド バイト シーケンスとしてエンコードされた辞書のハッシュを含む Available-Dictionary ヘッダーを送信します。
Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:
この時点で、一致する辞書が使用されるように、サーバー側で圧縮を構成する必要があります。その辞書で圧縮されたリソースが送信され、ユーザーのブラウザ キャッシュにある利用可能な辞書を使用して解凍されます。
ウェブサイトの新しいコードを頻繁にリリースする場合、デルタ圧縮は非常に有効です。ただし、このプロセスは柔軟です。ブラウザがユーザーのブラウザ キャッシュで辞書が利用可能であることを判断しない場合、Accept-Encoding ヘッダーに追加の br-d トークンまたは zstd-d トークンを指定しません。 その場合、標準の圧縮フローが適用されます。
動的リソースの共有辞書圧縮
動的リソースも、共有辞書圧縮のメリットを享受できます。動的リソースとは、コンテキストに基づいて変化するリソースです。たとえば、ニュース速報が流れるたびにメインページが頻繁に更新されるニュース ウェブサイトなどです。HTML ドキュメントは動的リソースであることがよくあります。このような場合、辞書にはサイトの一般的な HTML 構造とテンプレート コードのほとんどを含めることができ、各ページの固有の部分のみが送信される圧縮ページになります。
動的に生成されるリソースの性質上、辞書は後で使用するためにクライアントに読み込む必要があります。辞書を事前に読み込むと、動的リソースに共有辞書圧縮を適用することは推測になります。このような場合、ウェブサイトに十分なトラフィックが流れ、辞書のコストを多数のナビゲーションに分散できることが期待されます。試してみる場合は、まずページの HTML で <link> 要素を使用して辞書の場所を指定します。
<link rel="dictionary" href="/dictionary.dat">
Chrome はこの <link> 要素を検出すると、ページのアイドル状態になったら、帯域幅の競合を避けるために低優先度で辞書を取得します。辞書自体のレスポンスでは、Use-As-Dictionary ヘッダーを指定し、適用する動的リソースパスを指定する必要があります。
Use-As-Dictionary: match="/product/*"
ここから、フローは静的リソースの場合とほぼ同じです。ブラウザは、辞書自体が一致するリソースに適用されることを認識し、ブラウザは、前述の静的リソース フローと同様に、辞書の内容のハッシュを含む Available-Dictionary ヘッダーをリクエストに添付します。
ビルド時に静的リソースを圧縮する
バンドラに精通している場合は、ビルド時にリソースを圧縮し、圧縮されたリソースを配信できるさまざまなプラグインをご存じかもしれません。たとえば、Apache では、ディレクティブを使用して、リクエスト時に事前圧縮されたリソースを配信できます。
圧縮をサポートする Node.js ベースのバンドラのほとんどは、Node の組み込み Zlib ライブラリを使用します。Zlib は Brotli をサポートしており、これを使用するバンドラは通常、辞書支援圧縮をサポートする Zlib にオプションを直接渡すインターフェースを提供します。辞書の使用をサポートするバンドラをいくつか紹介します。
- webpack の
CompressionWebpackPlugin(compressionOptionsインターフェースを使用)。 rollup-plugin-brotliは、Node.js の Zlib に直接渡されるoptions構成を提供します。ここで辞書を指定できます。- esbuild の
esbuild-plugin-compressサードパーティ プラグイン も、Node.js の Zlib オプションへのアクセスを提供します。
リソースの特定のバージョンで使用可能な辞書は、リソースの以前のバージョンのいずれかを使用する場合があります。つまり、ユーザー トラフィックを分析して、それに応じて計画する必要があります。バランスを取り、できるだけ多くのリピーターにメリットをもたらすリソースを生成することを目指してください。CDN プロバイダは現在、共有辞書圧縮をテストしています。まだ一般公開されている実装はありませんが、変更される予定です。
試してみる
共有辞書圧縮をブラウザの既存の圧縮機能と統合することで、更新されたプロダクション コードを頻繁にリリースし、リピーターからのトラフィックが多いウェブサイトの読み込みパフォーマンスを大幅に改善できる可能性があります。共有辞書圧縮を試してみたい場合は、次の 2 つの方法があります。
- 共有辞書圧縮を自分で試して、その仕組みを理解したい場合は、
chrome://flagsページで [圧縮辞書転送] 試験運用版機能を有効にできます。 - 本番環境のウェブサイトでこれを試して、共有辞書圧縮が実際のユーザーにどのように役立つかを確認したい場合は、オリジン トライアルに登録してトークンを取得し、オリジン トライアルの仕組みをご覧ください。
まとめ
ウェブでの圧縮技術のこの大きな進歩と、ユーザーが毎日使用する既存のアプリケーションをどれだけ高速化できるかについて、私たちは非常に期待しています。ぜひお試しください。最も重要なことは、試してみた感想をお聞かせいただきたいということです。バグを見つけた場合は、crbug.com で報告してください。その他のリソースとツールについては、use-as-dictionary.com をご覧ください。最後に、仕組みについて詳しく知りたい場合は、解説が次のステップとして最適です。