見えないバグを追いかける

予測できず、再現性がなく、自分のものではなく、実質的に見ることができないバグを見つけるには、どうすればいいのでしょうか。

まずは、シナリオを決めることです。そのために、ユーザーが認識できるジャンクに注目します。そして Chrome が遅いと感じる瞬間をシステム的に突き止める方法として、ジャンクを実際の環境で測定します。

次に、実用性の高いバグレポートを実際の環境で集めます。そのために、Chrome の BackgroundTracing インフラストラクチャを使って Slow Report と呼ぶものを生成しました。匿名で指標を共有することに同意した一部の Canary ユーザーで、特定のシナリオを調査できる循環バッファ トレースを有効にします。すると、注目する指標があらかじめ設定されたしきい値に達したときに、トレース バッファが取得され、匿名化が行われて Google のサーバーにアップロードされます。

このバグレポートは、次のようなものです。


通常は健全なマシンで、AutocompleteController::UpdateResult() の 2 秒のジャンクを chrome://tracing で表示したもの


犯人がわかりました。AutocompleteController を最適化すればいいのですね。いや、違います。まだ理由がわかっていないのです。何の前提も設けないようにしましょう。

BackgroundTracing をスタック サンプルで補足すると、ストールした AutoComplete イベント内で繰り返し起こっているスタックを見つけることができました。
    RegEnumValueW

    RegEnumValueWStub

    base::win::RegistryValueIterator::Read()

    gfx::`anonymous namespace\'::CachedFontLinkSettings::GetLinkedFonts

    gfx::internal::LinkedFontsIterator::GetLinkedFonts()

    gfx::internal::LinkedFontsIterator::NextFont(gfx::Font *)

    gfx::GetFallbackFonts(gfx::Font const &)

    gfx::RenderTextHarfBuzz::ShapeRuns(...)

    gfx::RenderTextHarfBuzz::ItemizeAndShapeText(...)

    gfx::RenderTextHarfBuzz::EnsureLayoutRunList()

    gfx::RenderTextHarfBuzz::EnsureLayout()

    gfx::RenderTextHarfBuzz::GetStringSizeF()

    gfx::RenderTextHarfBuzz::GetStringSize()

    OmniboxTextView::CalculatePreferredSize()

    OmniboxTextView::ReapplyStyling()

    OmniboxTextView::SetText...)

    OmniboxResultView::Invalidate()

    OmniboxResultView::SetMatch(AutocompleteMatch const &)

    OmniboxPopupContentsView::UpdatePopupAppearance()

    OmniboxPopupModel::OnResultChanged()

    OmniboxEditModel::OnCurrentMatchChanged()

    OmniboxController::OnResultChanged(bool)

    AutocompleteController::UpdateResult(bool,bool)

    AutocompleteController::Start(AutocompleteInput const &)

    (...)


なるほど。オートコンプリートが悪いわけではありません。GetFallbackFonts() を最適化すればいいのですね。でも、待ってください。そもそも、いったいどういうわけで GetFallbackFonts() が呼ばれているのでしょうか。

それを突き止める前に、どうすればこれがパフォーマンスのロングテール問題全体の一番の根本原因だとわかるのでしょうか。とにかく、まだ 1 つのトレースしか見ていないのです ...



測定における難問

指標からは、どのくらいのユーザーが影響を受けているか、どの程度悪い状態なのかはわかります。しかし、根本原因がわかるわけではありません。

Slow Report からは、特定のユーザーの問題はわかりますが、どのくらい多くのユーザーが影響を受けているかはわかりません。また、Slow Report トレースのコーパスを検索することはできますが、これには本質的にバイアスがかかっているので、指標と 1 対 1 で対応することは不可能です。たとえば、Chrome はセッション 1 つにつきパフォーマンス悪化の最初の事例だけをレポートし、対象も Canary/Dev チャンネルのユーザーだけなので、起動と母集団の両方のバイアスがかかっています。

これは測定における難問です。ツールが提供するデータの実用性が高いほど、取得できるシナリオは少なくなり、強いバイアスがかかるようになります。深さをとるか、広さをとるかです。

両方を行おうとするツールはその中間にあたります。その場合、大きなデータセットを集計するので、欠陥のある入力に基づく結果を集計してしまうというリスクがあります(たとえば、注目したい部分が循環バッファ トレースから欠落しており、バイアスがかかった集計になるなど)。


そこで、科学的理論に基づき、最もエンジニアリング的でない選択肢を選びました。つまり、大量の Slow Report のトレースを手動で開くという方法です。これは、すでに定量化できている最重要な問題に対して、最も効果的な手法になりました。

たくさんのトレースを開いた結果、そのほとんどに、なんらかの形で前述のフォントの問題が現れていることがわかりました。影響を受けた厳密なユーザー数はわかりませんが、指標に現れていたユーザーの苦しみの主な原因はこれだと確信するには十分でした。




フォールバック フォント

そもそも GetFallbackFonts() が呼ばれる理由は何なのかを追求しました。先ほどの例の呼び出し元は、あるフォントでレンダリングされる Unicode 文字列のピクセル数を求めようとしていました。

その中のサブ文字列に、指定されたフォントではレンダリングできない Unicode ブロック内の文字がある場合、システムが推奨するフォールバック フォントをリクエストするため、GetFallbackFont() が使われます。それに失敗すると、リンクされているフォントをすべて試してレンダリングに最適なものを決めるため GetFallbackFonts() が呼び出されます。この 2 回目のフォールバックは遅くなります。

GetFallbackFont() が失敗することはないはずですが、実際はそこまで単純ではありません。Windows でこれを確実に行う方法は、DirectWrite に照会することです。しかし、DirectWrite は Chrome がまだ Windows XP をサポートしていたころの Windows 7 で追加されたものでした。そのため、両方のバージョンの OS で動作するように、GetFallbackFont() のロジックで確実性が低い試行錯誤的な Uniscribe+GDI を利用せざるを得ませんでした。それでもほとんどの場合はうまく動作したので、のちに Chrome で Windows XP のサポートが削除されたときも、この処理をクリーンアップできることに誰も気づきませんでした。パフォーマンスのロングテールを調査する新しいツールを使うことで、ジャンクの一番の原因(GetFallbackFonts() の不要な呼び出し)が明らかになったのです。

Google はこれを修正し、GetFallbackFonts() の呼び出し回数を 4 分の 1 に削減しました。


まだゼロではないので、前述の AutoComplete の問題は引き続き Slow Report で確認できます。そのため、調査を続けましょう。DirectWrite の GetFallbackFont() の失敗は予期しないものでしたが、Slow Report は匿名化されているので、ユーザーが生成した文字列はアップロードできません。そのため、どのコードポイントが問題を起こしているのかを突き止めるのは難題です。そこでプライバシーのエキスパートとも相談し、個人を特定できる情報が漏洩しないように、Unicode ブロックとテキスト ブロックのスクリプトを HarfBuzz に通すことにしました。


 

絵文字の物語

この新しい記録が利用できるようになるとともに、Slow Report の次の波がやってきました。大半のレポートでは、DirectWrite に Miscellaneous Symbols and Pictographs(その他の記号とピクトグラフ)内のコードポイント(Unicode 文字)のフォントを見つけるようリクエストしたときに、フォントのフォールバックが失敗していました。ローカルでその Unicode ブロックのすべてのコードポイントを試すスクリプトを書いたところ、問題を起こしていたのは何かがすぐにわかりました。U+1F3FB~U+1F3FF は、Unicode 8.0 で追加された修飾子で、別のコードポイントと組み合わせたときのみ意味を持ちます。たとえば、U+1F9D7(🧗)と U+1F3FF を組み合わせると 🧗🏿 となります。U+1F3FF 自体をレンダリングできるフォントはありません。そのため、フォントのフォールバックに正しいフォントを見つけるよう依頼しても、すべてのリンクされているフォントを調べた後にエラーになるのは正しい動作です。グラフこれはブラウザ側の Unicode セグメンテーション ロジックのバグでした。バグによって 2 つのコードポイントが誤って分割されるため、1 つの書記素としてではなく、別々にレンダリングするように DirectWrite にリクエストしていました。

でも、待ってください。Chrome は最新の Unicode をサポートしているのではなかったでしょうか。確かに、ウェブ コンテンツをレンダリングする Blink はサポートしています。しかし、ブラウザ側のロジックは、絵文字を描画することはないので、最新の絵文字(修飾子付きのもの)をサポートするように更新されてはいませんでした。ブラウザの UI(タブバー、ブックマーク バー、アドレスバーなど)が最新化され、Unicode をサポートするようになったのは、2018 年ごろになってからのことです。そのときから、以前のセグメンテーション ロジックが(見えない)問題になっていました。

そのうえ、キャッシュ ロジックはエラー時にキャッシュを行わないようになっていたので、たくさんのフォントがインストールされたユーザーでは、修飾子を自力でレンダリングしようとするたびに大きなジャンクが起きていました。皮肉なことに、このキャッシュは、ブラウザ UI に初めて Unicode サポートが追加されたとき、誤解されたボトルネックに対処するために追加されたものでした。フォント API のレイヤーでとどまるのではなく、フォントのロジックについて下層の実装に迫り続けたことが、主要なパフォーマンスの問題の修正だけでなく、他の絵文字に関する修正にもつながりました。たとえば、🏳️‍🌈 をコードで表すと、U+1F3F3(🏳️)+U+1F308(🌈)となります。分割ロジックを修正するまで、この書記素はブラウザの UI で 🏳️🌈 と誤ってレンダリングされていました。



そして旅は続く …

Google の旅は、さまざまな Chrome のコンポーネントに迫り続けています。しかしそれは、いつも同じ基本戦術に従っています。それは、何の前提も設けないようにして、予想できず、再現できず、自分のものでもないバグを徹底的に追求することです。スタック ランキングの問題は不可能に近いですが(参照 : 測定の難題)、なんらかのツールで見つけたトップ 5 の問題を修正し、ロングテールに注目すれば、実際のユーザーの苦しみの大半に対処できることになります。

Google はこのアプローチによって、ここ 2 年半の間でユーザーの目に見えるジャンクを 10 分の 1 に減らし、狙いを定めた多くの機能でパフォーマンスのロングテールを改善しました。

30 秒間のサンプルにおいて 100 ミリ秒間隔で無応答になった数の 99 パーセンタイル


すべての統計情報の出典 : Chrome クライアントから匿名で集計した実データ

Reviewed by Eiji Kitamura - Developer Relations Team


メモリ管理の改善

Windows 版の Chrome M89 では、ブラウザ プロセスで最大 22%、レンダラで 8%、GPU で 3% と、大幅なメモリの節約を実現しました。さらに、ブラウザの応答性が最大 9% 改善しています。これは、Google の高度なメモリ アロケータである PartitionAlloc を使って実現しました。このアロケータは、割り当ての低遅延性、空間効率、セキュリティの面で最適化されています。PartitionAlloc は、これまでにも、Google のレンダラ コードベースである Blink の内部で広く使われています。M89 より、Android 版と 64 ビット Windows 版の Chrome をアップグレードし、(malloc をインターセプトして)すべての場所で PartitionAlloc を使うようにしました。

現在の Chrome は、メモリの割り当て方法が改善されただけでなく、メモリの使用(と破棄)についてもさらにスマートになりました。現在の Chrome は、画面外の大きなイメージなど、フォアグラウンド タブが使っていないメモリを破棄することで、タブ 1 つにつき最大 100 MiB を回収します。人気サイトの 20% 以上がこのようなサイトに該当します。macOS 版の Chrome では、バックグラウンド タブのメモリ フットプリントも縮小しています。これは、他のプラットフォームでしばらく前から行っていることです。これにより、最大 8%、場合によっては 1 GiB 以上のメモリを節約できます。

さらに、タブ スロットリングに関する実データが集まるにつれて、バックグラウンドのタブの Apple Energy Impact スコアが最大 65% 改善しています。つまり、Mac は熱くならず、ファンも静かになっています。


ビルド、パッケージング、ランタイムの改善

Chrome for Android に特化して、あらゆる種類の Android デバイスにとって快適なブラウザを作るという、他にはない挑戦をしています。今回のリリースでは、パッケージングとランタイムの最適化を適用し、ポケットの中の Chrome のパフォーマンスを向上させています。


新しい Play 機能や Android 機能を使うと、Android で Chrome を再パッケージングできます。これにより、リソースの枯渇によるクラッシュが減少し、メモリ使用量は 5%、スタートアップ時間は 7.5%、ページ読み込みは最大 2% 改善しました。Android App Bundle を使うと、各ユーザーのデバイス構成に合わせて最適化した APK を Play ストアで生成できます。コードやリソースを分割 APK にパッケージングして、ベース APK とともにインストールすることもできます。また、Android O の機能である isolatedSplits を使うと、これらの分割機能をオンデマンドで読み込むことで、Chrome 全般のスタートアップ コストを削減できます。

最新の Android デバイス(Android Q 以降かつ 8 GB 以上の RAM)をお使いの方のために、Chrome を 64 ビットバイナリとして再ビルドしました。これにより、ページの読み込みが最大 8.5% 速く、スクロールや入力のレイテンシが 28% 少なくなり、さらに安定した Chrome を提供できるようになっています。

加えて、Android で Chrome の起動が 13% 速くなる機能、フリーズドライ タブを組み込んでいます。この機能により、軽量版のタブが保存されるようになり、このタブのサイズはスクリーンショットと同じくらいでありながら、スクロール、ズーム、リンクのタップをサポートします。スタートアップ時には、実際のタブがバックグラウンドで読み込まれるまでの間、このフリーズドライ タブを利用してページにすばやくアクセスできます。以下の動作例をご覧ください。



Google のチームは、あらゆるデバイスに最速、最強のブラウザをお届けするために、常に懸命に作業を進めています。ここでお知らせしたパフォーマンスの改善を提供できることをうれしく思います。今後もさらに改善を続けますので、ご期待ください。

すべての統計情報の出典 : Chrome クライアントから匿名で集計した実データ


Reviewed by Eiji Kitamura - Developer Relations Team


Reviewed by Shunya Shishido - Web Ecosystem Consultant, gTech


Milestone

ページのローディングなど、開始から完了までにどの程度の時間がかかったのか計測する指標。First Contentful PaintTime to InteractiveSpeed Index などの指標がそれにあたります。実際の UX とスコアを近いものにするために、ユーザー中心のパフォーマンス指標を主要なバジェットとして設定することを奨励します。

Quantity
アセットや通信量などの量を基準にした指標。JavaScript のファイルサイズや HTTP リクエストの数、クリティカルレンダリングパスの数といった値の上限をバジェットとして設定します。

Rules
PageSpeed InsightsLighthouse、もしくは WebPageTest などのパフォーマンス測定ツールで算出されたスコア。




上記の図は、ネットワーク状況に応じたバジェット内でのリソース配分と FCP/TTI のスコア設定の一例です。実際にサービスにバジェットを設定する際に参考にしてください。

これらのメトリクスをパフォーマンスバジェットに設定しているサービスには、Pinterest があります。JavaScript のファイルサイズを 200 KB に抑えた上で 3G 環境で Time to Interactive を 6 秒以内に短縮し、収益が 44% 向上しました。また、Tinder はメインとなる JS ファイルを 160 KB に、非同期で読み込む JS の chunk ファイルを 50 KB に抑えるようにバジェットを設定したうえで、Time to Interactive を Pinterest と同じく 6 秒以内に短縮することで、ネイティブアプリよりもユーザーがより多くのスワイプをするという結果に繋げました。


パフォーマンスの管理が必要な理由

一度高速なウェブサイトを作ったとしても、パフォーマンスを維持するのは想像以上に困難です。新しい機能の追加や、サードパーティの計測タグの設定、意図せず巨大なファイルサイズの写真を配信してしまった等、長期的に見ると開発をする中で起きる大小さまざまな出来事によりパフォーマンスは劣化してしまいます。


一方で、モバイルサイトの速度とビジネス指標には相関関係があり、1 秒表示速度が改善されると、コンバージョン率が 27 % 改善されるというデータが存在します。また、E コマースサイトでのテストニュースサイトにおけるテスト結果からもサイトを高速に保つことがビジネス上重要であることがおわかりいただけると思います。


パフォーマンスバジェットを管理する

パフォーマンスバジェットを定義したあと、どのように運用していけばよいのでしょうか。開発フローの中にバジェットを組み込むためのツールをいくつか紹介します。


Lighthouse Bot




https://github.com/GoogleChromeLabs/lighthousebot

Lighthouse Bot (旧Lighthouse CI) は、Github の Pull Request 単位で Lighthouse を実行、パフォーマンスやベストプラクティス、PWA といった Lighthouse の各スコアをパスした場合のみマージされるように制限することが可能になります。Lighthouse のルールベースのメトリクスをバジェットに設定しているときに有効です。

bundlesize

https://github.com/siddharthkp/bundlesize

bundlesize は、アプリケーションのアセットファイルサイズをチェックするツールです。JavaScript のファイルサイズ等、アセットの量を制限したい場合に有用です。

SpeedCurve




https://speedcurve.com/

継続的パフォーマンス モニタリングツールの SpeedCurve は、パフォーマンスバジェットの設定ができます。包括的な設定が可能で、Speed Index や First Contentful Paint といったマイルストーンベースのメトリクスや、JavaScript のファイルサイズ、HTTP リクエストの数といった量ベースのメトリクス、さらには Lighthouse を使った計測も行うのでルールベースのメトリクスもバジェットとして設定可能です。また、バジェットを超過した際に Slack への通知といった機能も持っています。

その他にも、PageSpeed Insights API や WebpageTest を使って取得したデータを Google Sheets に記録、Google データポータル(旧データスタジオ)と連携して可視化するといった方法でもバジェットの運用は可能です。これらのサービスを組み合わせて作られている speed-demon は、コーディング不要ですぐに利用できます。

まとめ
パフォーマンスバジェットの基本的なコンセプトや管理方法について紹介しました。より詳細な解説は、web.dev の Enforce performance budgets をご覧ください。また、先日の Chrome Tech Talk Night #12 でもパフォーマンスバジェットについてトークセッションを行いました。イベントの様子はこちらから視聴できますので、より詳しく知りたい方はこちらも合わせてご参照ください。


Posted by Shunya Shishido, Mobile Technical Solutions Consultant
Share on Twitter Share on Facebook


プロジェクト Orinoco の取り組みによってコンカレント ガベージ コレクションを実現し、メインスレッドが解放されてジャンクが減りました。その後は、重点領域を実環境での JavaScript のパフォーマンスに移し、React.js ランタイムのパフォーマンスが 2 倍になり、Vue.js、Preact、Angular などのライブラリのパフォーマンスが最大 40% 改善しました。並列、コンカレント、増分ガベージ コレクションにより、ガベージ コレクションによるジャンクは最初に V8 がコミットされたときに比べて 100 分の 1 まで減少しています。さらに、WebAssembly も実装し、デベロッパーがウェブで JavaScript 以外のコードを予測可能なパフォーマンスで実行できるようにしました。WASM アプリを確実に高速起動できるように、Liftoff ベースライン コンパイラもリリースしました。V8 のパフォーマンスはリリースごとに徐々に増加し、長年かけて 20 倍のパフォーマンスを達成しました。上記の新しいコンポーネントは、その 10 年間積み重ねた努力の中で、まさに最新のコンポーネントです。

これまでの Chrome リリースでの V8 Bench スコア。V8 Bench は、旧 Octane ベンチマークの前身。この図で V8 Bench を使っているのは、Octane と違って初期の Chrome ベータ版を含むすべてのバージョンの Chrome で動作するため。

Chrome は、SPDYHTTP/2QUIC を通して、ネットワーク プロトコルやトランスポート層の進化に重要な役割を果たしています。HTTP/1.1 の制限に対処することを目的とした SPDY は、現在すべてのモダンブラウザがサポートしている HTTP/2 プロトコルの土台となりました。同時に、レイテンシとユーザー エクスペリエンスをさらに改善することを狙った QUIC にも積極的に取り組み、活発な IETF もこの取り組みをサポートしています。QUIC は、YouTube などの動画サービスに大きなメリットをもたらします。QUIC で動画を視聴すると、再バッファリングが 30% 減少することがユーザーから報告されています。

次に紹介するのは、Chrome のレンダリング パイプラインです。レンダリング パイプラインの役割は、ウェブページがユーザーの操作にすばやく反応し、毎秒 60 フレーム(fps)で表示されるようにすることです。Chrome がコンテンツを 60fps で表示するには、1 つのフレームを 16 ミリ秒でレンダリングしなければなりません。これには、JavaScript の実行、スタイルの設定、レイアウトの決定、ピクセルの描画とユーザーの画面への表示などが含まれます。この 16 ミリ秒のうち、Chrome が使う時間が少なければ少ないほど、ウェブ デベロッパーがユーザーの満足度を上げるために使える時間は増えます。レンダリング パイプラインの改善には、再描画が必要になるページ要素の特定方法の最適化や、視覚的に重ならない要素のトラッキングの向上などが含まれます。これによって、画面に新しいフレームを描画する時間が最大 35% 短縮されました。

Chrome チームは 2015 年に RAIL というユーザー中心のパフォーマンス モデルを導入し、先日このモデルが更新された。

メモリ消費量はどうでしょうか。Chrome 63 から 66 の間で、レンダラー プロセスのメモリ使用量が最大 20~30% 削減されています。サイト分離が導入されたので、今後、このメモリ削減に基づいて行うことを模索する予定です。Ignition と TurboFan も、V8 の合計メモリ使用量の削減に貢献しており、V8 をサポートするすべての端末やプラットフォームで 5~10% のメモリが節約できています。今年行われた分析から、ウェブ上のサイトの 7% がメモリリークの影響を受けていたことがわかりましたが、これは完全に修正されています。Chrome のスピードには、DOM や CSS、IndexedDB を始めとするストレージ システムなど、多くのコンポーネントが貢献しています。パフォーマンス改善の詳細については、Chromium ブログの最新記事にご注目ください。

ウェブ デベロッパーによるウェブページの計測や最適化を強化

どこからサイトの改善を始めればよいのかを理解するのは、大変な作業です。その手助けとして、ラボ環境における状況や、ユーザーが実環境で感じる体験について把握できるいくつかのツールを検討しました。長年かけて、Chrome DevTools の Performance パネルは、ウェブページがラボ環境でどのように表示されるかを順を追って視覚化できる強力な方法となりました。サイトでパフォーマンスを改善する余地を見つける手間をさらに軽減するために、Lighthouse の開発も行いました。これは、ウェブサイトの品質を分析し、サイトのパフォーマンスに関する明快な指標とユーザー エクスペリエンスを改善するための具体的な方法を提示してくれるツールです。Lighthouse は、DevTools の Audits パネルを通して直接利用できるほか、コマンドラインからも実行できます。また、WebPageTest などの他の開発用プロダクトにも組み込まれています。
Chrome DevTools の Audits パネルで Lighthouse を実行



Lighthouse によるラボ環境のデータを補完するために、Chrome ユーザー エクスペリエンス レポートをリリースし、ユーザーが実環境で感じる体験の指標となる項目をデベロッパーに提供できるようにしました。レポートには、First Contentful PaintFirst Input Delay などの項目が含まれています。現在、デベロッパーはカスタムサイトのパフォーマンス レポートを作成し、CrUX Dashboard を使って莫大な数のオリジンに対する進捗をトラッキングできるようになっています。

また、たくさんの Web Platform 機能を導入し、デベロッパーがサイトの読み込みを最適化できるようにしています。早い段階で読み込む必要があるリソースをブラウザに伝えられるように、リソースヒント<link rel=preload> を追加しました。Chrome は、Brotli 圧縮WOFF2 ウェブフォント圧縮、イメージの WebP サポートなど、バイト削減をサポートするアプローチを実装したブラウザの先駆けでもあります。

今後、ますます多くのブラウザでこれらの機能がサポートされることを楽しみにしています。Chrome は Service Worker を実装しているので、何度もページにアクセスする際にオフライン キャッシュやネットワーク レジリエンスを活用できます。この機能が幅広くモダンブラウザでサポートされていることをうれしく思います。


実際に、現在の Google 検索は、繰り返し検索を行う際の便宜的キャッシュに Service Worker とナビゲーション プリロードを使っています。これにより、繰り返しアクセスする場合のページの読み込み時間が半分になっています。

将来的に、イメージや iframe のネイティブ遅延読み込み、AV1 などの画像形式を含む新しい標準によって、ユーザーに対して効率的にコンテンツを提供できるようになるでしょう。今からとても楽しみです。

Chrome を使ってデータプラン内でウェブをもっと楽しむ


ここ 10 年間で、ウェブページのサイズは増加の一途をたどっています。しかし、初めてオンラインの世界にデビューする多くのユーザーにとっては、データは法外に高価だったり、耐えられないほど遅い可能性もあります。それに対応するため、ここ数年で、Chrome データセーバーなどのデータを意識した機能がリリースされてきました。データセーバーは、インテリジェントにページを最適化し、データ消費量を最大 92% 削減します。

さらに、データを節約する新しい方法も模索しています。Chrome for Android を対象に、接続速度が遅いユーザーのために、ページをスマートに最適化して重要なコンテンツが最初に表示されるようにする機能の開発を進めています。このようなページ変換によって、完全なページよりもはるかに早く読み込めるようになります。この機能については、再現性、網羅性、パフォーマンスをさらに向上させる作業を続けています。

データやネットワークに制限のあるユーザーのために、ガードレールを設ける実験も行っています。たとえば、Chrome にネイティブ遅延読み込みを導入する方法や、たくさんのデータを使った場合にページの追加リクエストを停止するオプションをユーザーに提供する方法を模索しています。

まだ始まったばかり

これらの変更点は、デベロッパーや企業がユーザーに便利なコンテンツをすばやく提供する際に役立ちます。しかし、まだやるべきことがあります。今後 10 年間で行われるページ読み込みパフォーマンスの改善に乾杯しましょう!


Reviewed by Eiji Kitamura - Developer Relations Team

Share on Twitter Share on Facebook

Fast Fetch は、Delayed Fetch と比較して 50 パーセンタイルで 850 ミリ秒、90 パーセンタイルで 2.7 秒高速です。


AMP Ads の協調レンダリング

広告レスポンスが AMP 形式である場合(AMP Ads)、AMP ランタイムは広告を即座にレンダリングします。レスポンスが通常の広告である場合、ランタイムはページの残りのコンテンツが読み込まれるのを待つ必要があります。AMP 広告ではパフォーマンスが保証されているのでそのようなことが可能ですが、非 AMP 広告にはそのような保証はありません。



DoubleClick と AdSense の実験によると、AMP Ads は 50 パーセンタイルで 1.6 秒、90 パーセンタイルで最大 5 秒早く読み込まれます。


広告が画面に早く表示されるほど、広告の視認性も向上します。ブランドが多くの人の目に触れることになるので、これは広告主にとって有益です。また、視認性の向上によってユーザーが広告を開くチャンスが増えるため、成果型の広告主にとっても有益です。

Fast Fetch の新機能のリリース

多くのサイトオーナーが、主要サイトのコンテンツを AMP 形式で提供する実験を行っています。そのようなサイトオーナーの作業をサポートするため、AMP の Fast Fetch には、さらに高度な広告機能が追加される予定となっています。たとえば、次のようなものです。

サイトオーナー(または広告主)の皆さまへ

DoubleClick と AdSense のおかげで、AMP ページから広告リクエストが送られる際に、一定の条件を満たす広告は自動的に AMP に変換されます。AMP に変換可能な形式が増えるにつれて、自動変換される広告も増加することが考えられます。その結果、何も変更しなくても、皆さまのページに高い視認性とクリックスルー率を持つ広告が提供されることになります。

クリエイティブを制作している広告主(またはサイトオーナー)の皆さまへ

広告を制作している方は(サイトオーナーであるか広告主であるかにかかわらず)、AMP Ads への切り替えをご検討ください。視認性やユーザー エクスペリエンスが高い高速な広告によるメリットを受けることができます。まずは、社内のクリエイティブ制作者に、こちらの AMP Ads デベロッパーのよくある質問を確認してもらってください。

クリエイティブ アセットの制作を外注している場合は、AMP クリエイティブの制作に特化した JoyStick Interactive などの代理店に依頼することができます。広告制作ツールを使ってアセットを作成している場合は、Celtra の Ad Creator を使って AMP Ads を制作することをご検討ください。Google Web Designer などの他のツールでも、近日中に AMP 広告がサポートされます。

広告技術プラットフォームの皆さまへ

DoubleClick や Adsense の広告タグは、Fast Fetch を利用して劇的にレイテンシを削減しています。私たちは、すべての広告ネットワークが Fast Fetch に移行することを望んでいます。移行をサポートするガイドはこちらです。AMP Ads に署名したい方は、Cloudflare の Firebolt サービスを利用できます。自分で署名したい方は、Github をご覧ください。

「Cloudflare Firebolt を使うと、どんな広告ネットワークでも、署名した広告を世界に提供できます。追加の作業はほとんど発生しません」と Cloudflare のプロダクト戦略責任者である Dane Knecht 氏は述べています。「私たちは、インターネットをよりよくするという使命の一環として、Firebolt のグローバルな AMP 広告エクスペリエンスをさらに高速で安全なものへと強化しています。それによって、コンバージョン率を向上させることができます」


また、AMP Ads は DoubleClick Ad Exchange(Real Time Bidding 経由)をサポートしており、DSP は RTB 経由で AMP 広告を提供できるようになっています。

AMP での広告は大きく進化しており、フェーズ 3 に入ったことをうれしく思っています。このフェーズでは、次のようなことを行う予定です。 





いつものように、皆さまのフィードバックは大歓迎です。ウェブの進化に向けて、上記の取り組みにご協力いただける方もお待ちしています。


Reviewed by Yoshifumi Yamaguchi - Developer Relations Team
Share on Twitter Share on Facebook



Firebase Performance Monitoring には、トレースとネットワーク アクティビティ監視という 2 つの主な機能があります。まずは、トレースについて説明しましょう。トレース機能を使うと、アプリの特定の操作にかかる時間を把握したり、「カウンター」API を使って操作にカスタムの指標を追加できます。たとえば、イメージの読み込みが始まってから画面でイメージのレンダリングが終わるまでの時間をトレースしたり、カウンターを使ってイメージ読み込み時のキャッシュのヒット回数やミス回数をトラッキングすることができます。

アプリの起動は SDK をインストールするだけで自動でトレースできるので、アプリのコールド スタートにかかる時間を監視できます。


Firebase Performance が提供するもう 1 つの機能は、ネットワーク アクティビティの監視です。アプリが実行する HTTP/S リクエストは、送信されてから応答を受信するまで、自動的に監視されます。URL のパターンごとに、応答時間、ペイロード サイズ、成功率を確認できます。


リクエストに失敗した場合は、原因となったレスポンス コードについて 400 番台と 500 番台の内訳を確認できます。



収集されるトレースおよびネットワーク関連の全指標では、国、端末、アプリのバージョン、OS のバージョンの内訳を確認できます。これによって問題の原因を絞り込めるので、修正が簡単になります。



現在、Firebase Performance Monitoring はベータ版として公開されています。詳しくはこちらのドキュメントまたは I/O セッションをご覧ください。



Reviewed by Khanh LeViet - Developer Relations Team
Share on Twitter Share on Facebook

以前は、入力カーソルの点滅といったあらゆる画面のアップデートによって、タイル全体が再レンダリングされていました(左)。現在は、一部のピクセルのみが変更された場合、変更された領域のみを再描画します(右)。


インテリジェントなワークロードの最適化に加え、基盤となるハードウェアをうまく選択して処理効率をあげる仕組みも導入されています。video 要素や canvas 要素は 昔から GPU によって高速化されてきましたが、さらに高度なレンダリング タスクにも GPU を活用できるような改善が続けられています。Android、Mac、Windows の Chrome では、複雑なサイトのコンテンツをレンダリングする際にも GPU による高速化をうまく活用できるようになっています。これによって、最新の SVG ページや HTML5 ページでのアニメーションのパフォーマンス、入力の遅延が改善され、スクロールがスムーズになりました。

スピードにはさまざまな側面があります。私たちは引き続き、デベロッパーの皆さんがユーザー エクスペリエンスを最適化して 60fps を実現できるように、Chrome のパフォーマンス向上に取り組んでいきます。レンダリング パイプラインはパズルの 1 ピースにすぎません。この先には、まだ多くのなすべきことがあります。私たちは、すべての Chrome ユーザーが常に高速で反応の早いウェブを使えるようにするため、パフォーマンスについて深く探求し続けています。今後もご期待ください。


Posted by Eiji Kitamura - Developer Relations Team
Share on Twitter Share on Facebook



一見小さな変更ですが、この新しい動作によってリロードが 28% 高速になり、帯域幅と電池の消費量も少なくなります。また、Facebook から提供されたデータでは、Chrome は他のブラウザに比べ 3 倍のバリデーション リクエストを送信していました。新しいリロード動作とそれに関連するいくつかの変更によって、現在は Chrome でのページのリロードが 28% 高速になり、バリデーション リクエストが 60% 少なくなった ことが Facebook により報告されています。

お気に入りのウェブサイトから最新のコンテンツを取得したり、地下鉄で不安定な接続から回復する際、高速になったリロードが役立つことを願っています。


Posted by Takashi Toyoshima - Chrome Team
Share on Twitter Share on Facebook


ウェブ プラットフォームの改善により遅延を削減する方法がもう 1 つあります。JavaScript で効率的に実装できない共通の操作を、ブラウザが代わりにネイティブに構築することです。例えば、ある要素がドキュメントのビューポート内で表示可能か、別のスクロール コンテナにあるかの計算に使用されている大規模な JavaScript です。これは、特定の要素が表示されていたか、ページの下部に動的に追加コンテンツを読み込むかを知るために役立ちますが、ユーザーの操作で頻繁に実行されると、品質低下や不必要なバッテリー消費の原因となります。

Chrome 51 には、より効率が良く可視性情報を提供する新しい API、Intersection Observer が含まれています。この API を使えば、任意の要素が監視対象の要素またはその子要素と交差したら、コールバックを受け取ることができ、今後は独自の JavaScript で実装することなく、ページ読み込みやスクロール パフォーマンスの改善を実現することができるようになります。詳細情報と実装の詳細は、Intersection Observer の説明を参照してください。

調査  よると、パフォーマンスが向上するとユーザーの満足度が上がり、ユーザーはそのサイトに繰り返しアクセスするようになります。パフォーマンスは、ブラウザ実装者だけではなく、ウェブ製作者も含めたウェブ エコシステムのすべての人々が気にかける必要のあることです。トップレベルのサイトと埋め込みコンテンツの両方のデベロッパーがこれらの新しい API を使用すれば、すべてのユーザーに対する高速なウェブが実現します。

Posted by Eiji Kitamura - Developer Relations Team
Share on Twitter Share on Facebook



ガベージ コレクション タスク(赤色)をアイドル時間(青色)にスケジュールする。


スマートなガベージ コレクションを行うと遅延と途切れが減少し、3D アニメーションやゲームがスムーズに動作します。タスク スケジューラが V8 に統合されたことにより、ゲームプレイとレンダリングに対してさらに高いフレームレートが一貫してもたらされます。Chrome 41 から Chrome 46 までの間に、WebGL グラフィックのスムーズなレンダリングを計測する 3D ベンチマーク OORT Online のスコアが最大で 33% 増加しました。このスコアは、現在のデスクトップ コンピュータの最高スコア 10,000 ポイントをわずかに下回るだけです。




新しいシステムのメモリ消費量の低減は、ブラウザが数分間またはそれ以上アイドル状態になった場合、特に明白です。たとえば、Gmail タブが 2 分 30 秒の間未使用のままであると、 以前のバージョンに比較して Chrome 45 ではメモリ消費量が 最大で 45% 低減します。

よりスムーズで高速なブラウジング エクスペリエンスをユーザーに提供することに加え、これらの変更によってデベロッパーはさらに強力なアプリケーションと優れたグラフィックを使用して Chrome の限界を押し広げることが可能となります。今後の V8 の最適化にご期待ください。また、新しい V8 ブログガベージ コレクションのタイミングに関する詳細 をご覧ください。

Posted by Eiji Kitamura - Developer Relations Team
Share on Twitter Share on Facebook

対数目盛のグラフ
一度に 16 バイトのループ スキップが実行されるように簡単な変更を加えたことによって、プログラムの速度は 5 倍遅くなります

1,000 バイトから 100 万バイトの反復処理におけるパフォーマンスの平均差異は 5 倍であり、最大で 7 倍まで拡大することもあります。この変更はパフォーマンスにおいて深刻です。

注: このベンチマークは、デスクトップ(Intel 5930K 3.50GHz CPU)、Macbook Pro Retina ラップトップ(2.6 GHz Intel i7 CPU)、Android Nexus 5 および Nexus 6 デバイスなど、複数のハードウェア構成で実行され、どのハードウェア構成でも同じような結果になりました。

コンパイラーによっては宣言時に配列をキャッシュに保存するため、テストを再現するには、ループが実行される前に、キャッシュからメモリを解放する必要があります。処理の仕組みについては、以下で説明します。

説明

CPU がどのようにしてデータにアクセスするかを理解すれば、前述のサンプルで行われた処理内容を簡単に説明できます。CPU は RAM のデータにはアクセスできません。CPU チップの近くにある、小さくて極めて高速なメモリラインであるキャッシュにデータをコピーする必要があります。

プログラムが開始されると、CPU は配列の一部で命令を実行しますが、そのデータはまだキャッシュに存在しないため、キャッシュミスが発生し、データがキャッシュにコピーされるまで CPU は待機することになります。

説明をわかりやすくするために、16 バイトのキャッシュ サイズを L1 キャッシュ ラインと想定します。つまり、16 バイトは命令に対して要求されたアドレスからコピーされます。

最初のコードサンプルでは、プログラムは次に後続のバイトで処理を試みますが、最初のキャッシュミス後のキャッシュにすでにコピーされているため、処理はスムーズに継続されます。これは次の 14 バイトについても同様です。16 バイトの後は、最初のキャッシュミス以降、ループで別のキャッシュミスが発生し、CPU は次の 16 バイトをキャッシュにコピーして、再び処理対象のデータを待機します。

2 番目のコードサンプルでは、ループは一度に 16 バイトずつスキップしますが、ハードウェアは同じ処理を続けます。キャッシュはキャッシュミスが発生するたびに後続の 16 バイトをコピーするため、反復処理ごとにループでキャッシュミスがトリガーされ、CPU はそのたびにデータを待機するアイドル状態になります。

注: 最近のハードウェアでは、フレームごとのキャッシュミスを回避するキャッシュ プリフェッチ機構が実装されていますが、プリフェッチを実行した場合でも、このサンプルテストでは帯域幅の使用量が増え、パフォーマンスが低下します。
実際にはキャッシュラインは 16 バイトより大きくなる傾向があり、反復処理ごとにデータを待機する場合、プログラムの実行はさらに遅くなります。Nexus 5 の Krait-400 には 4 KB の L0 データ キャッシュ(1 行につき 64 バイト)があります。

キャッシュ ラインが非常に小さい主な理由は、高速なメモリを作るにはコストがかかるためです。

データ指向設計

このようなパフォーマンスの問題を解決する方法は、キャッシュに合わせてデータを調整し、プログラムが継続してデータ全体を処理するように設計することです。

これは、構造体の配列(AoS)の代わりに配列の構造体(SoA)にゲーム オブジェクトを整理し、予想されるデータを格納するために十分なメモリを事前に割り当てることで行えます。

たとえば、AoS レイアウトの単純な物理オブジェクトは次のようになります。
struct PhysicsObject
{
  Vec3 mPosition;
  Vec3 mVelocity;

  float mMass;
  float mDrag;
  Vec3 mCenterOfMass;

  Vec3 mRotation;
  Vec3 mAngularVelocity;

  float mAngularDrag;
};
これは、C++ でオブジェクトを表す一般的な方法です。

一方、SoA レイアウトを使用した場合は次のようになります。
class PhysicsSystem
{
private:
  size_t mNumObjects;
  std::vector< Vec3 > mPositions;
  std::vector< Vec3 > mVelocities;
  std::vector< float > mMasses;
  std::vector< float > mDrags;

  // ...
};
オブジェクトの位置を速度で更新する簡単な関数がどのように動作するかを比べてみましょう。

AoS レイアウトの場合、関数は次のようになります。
void UpdatePositions( PhysicsObject* objects, const size_t num_objects, const float delta_time )
{
  for ( int i = 0; i < num_objects; ++i )
  {
    objects[i].mPosition += objects[i].mVelocity * delta_time;
  }
}
PhysicsObject がキャッシュに読み込まれますが、使用されるのは最初の 2 つの変数のみです。それぞれ 12 バイトなので、反復処理ごとに使用されるキャッシュ ラインの 24 バイトと等しくなり、Nexus 5 の 64 バイトのキャッシュ ラインでオブジェクトごとにキャッシュミスが発生されることになります。

今度は SoA での処理を見てみましょう。これは今回使用する反復コードです。
void PhysicsSystem::SimulateObjects( const float delta_time )
{
  for ( int i = 0; i < mNumObjects; ++i )
  {
    mPositions[ i ] += mVelocities[i] * delta_time;
  }
}
このコードでは 2 つのキャッシュミスが即座に発生しますが、その次の 2 つのキャッシュミスが発生する前におよそ 5.3 回の反復処理をスムーズに実行できるため、パフォーマンスが大幅に向上します。

重要なのはハードウェアにデータを送信する方法です。データ指向設計に留意し、オブジェクト指向コードよりも効率的に実行される場所を探しましょう。

ここで紹介したのはほんの一例です。データ指向プログラミングには、オブジェクトの構造化以上に重要な点が多くあります。たとえば、キャッシュは命令や関数のメモリの格納に使用されるため、関数やローカル変数を最適化するとキャッシュミスやキャッシュ ヒットに影響を及ぼします。また、L2 キャッシュや、データ指向設計でアプリケーションのマルチスレッドを容易にする方法についてもここでは説明していません。

コードをプロファイルして、データ指向設計の実装対象を確認するようにしてください。NVIDIA Tegra System Profiler、ARM Streamline Performance AnalyzerIntel、PowerVR PVRMonitor など、アーキテクチャに応じて異なるプロファイラを使用できます。

キャッシュ用に最適化する方法の詳細については、各 CPU アーキテクチャのキャッシュ プリフェッチに関する記事をお読みください。


Posted by Yuichi Araki - Developer Relations Team
Share on Twitter Share on Facebook