Edited at

Chrome Developer Toolsでパフォーマンス計測・改善

More than 3 years have passed since last update.

Chrome Developer Toolsを使ったWebページのパフォーマンス計測・改善についての説明です

Networkパネル、Timelineパネル、Profilesパネルの使い方を説明してから

パフォーマンスの計測・改善について説明していきます


Networkパネル

Networkパネルはページのリクエストをしてからの通信内容の一覧を表示します

Network2.png


記録方法

左上のRecordボタンを押すと記録が始まる

もう一度押すと記録が停止する

必要に応じて、Disable cacheやCapture screenshotsを設定する


表示項目の変更

赤枠で囲んだ部分を右クリックすると

Network_項目.png

こんな感じでメニューが出てくるので表示したい項目をクリックする

項目の一例

 Name:リソースの名前

 Method:HTTPメソッドの種類

 Status:レスポンスのステータスコードとテキスト

 Type:リソースの種別

 Initiator:そのリソースがどこから読み込まれたか

 Size:リソースのサイズ

 Time:リソースのダウンロードにかかった時間

 Timeline:リクエストのダウンロード処理の詳細とかかった時間

Timelineをクリックするとソートの種類を選べます

Network_Timeline選択.png

・Start Time:リクエストの開始時間順

・Response Time:レスポンスの開始時間順

・End Time:完了した時間順

・Total Duration:トータル時間順

・Latency:リクエストを送信してから最初のレスポンスを受信するまでの時間(Waiting)順


フィルタリング

Network_フィルタリング.png

赤枠で囲んだ部分にフィルタリングのキーワードを入力すると、条件に合うものだけ表示される

データの種類別にフィルタをかけることもできる(All、XHRとかの部分)

「Hide data URLs」にチェックを入れるとdataURIのリクエストを非表示にできる

(blobについては非表示にできないようです)


リクエストの詳細

リクエストを選択しTimingタブをクリックすると詳細が表示される(Timelineにマウスを合わせてもOK)

Network_Timing.png

Timingタブは2つのフェーズに分かれている

Connection Setup(サーバとの接続をセットアップするフェーズ)

・Queueing:ネットワーク処理のキューイングに要した時間

・Stalled:プロキシのネゴシエーションを含んだ、TCPの接続制限による接続待ちなどによって発生する

     リクエスト開始までの時間

・Proxy Negotiation:プロキシサーバとの接続確立に要した時間

・DNS Lookup:DNSルックアップに要した時間

・Initial Connection:SSLやTCPのネゴシエーションを含めた初期接続確立までの時間

・SSL:SSLのハンドシェイクに要した時間

Request/Response(実際にデータのやりとりを行うフェーズ)

・Request sent:リクエストの送信に要した時間

・Waiting:リクエストを送信してから最初のレスポンスを受信するまでの時間

・Content Download:サーバからのレスポンスデータを受信するのにかかった時間

Timingタブ以外にもRequest/Response Headerの確認ができたり、Cookieの内容も確認できる

・Headersタブ:ホスト名とIPアドレス、クエリパラメータといったリクエストの基本情報および、

        リクエストヘッダとレスポンスヘッダが表示される

・Previewタブ:返却されたテキストやバイナリデータがプレビューされる

・Responseタブ:返却されたデータがパースされていない生の状態で表示される

・Cookiesタブ:リクエストとそのレスポンスに付与されたCookieが表示される


HAR(HTTP Archive)

Networkパネルのウィンドウ上で右クリックすると、↓このようなメニューが出てきます

SaveHAR.png

「Save as HAR with content」でHARファイルを保存

HTTP Archive Viewerで同じように解析できます

HTTPArchiveViewer.png


Timelineパネル

Timelineパネルではブラウザで起こった各種処理を時系列に記録しボトルネックがないか調査できます

Timelineパネル.png

各イベントの簡易説明

・青:Loading。読み込み、ネットワークの送受信、HTMLの解析

・黄:Scripting。スクリプトの実行、イベント処理、GC

・紫:Rendering。DOMの変更、ページのレイアウト、描画イベント

・緑:Painting。画像の処理


記録方法

左上のRecordボタンを押すと記録が始まる

もう一度押すと記録が停止する

必要に応じて、取得したい情報にチェックを入れておく(JS Profile、Memory、Paint、Screenshots)


フィルタリング

Filtering.png

イベントの名前、処理時間、イベントの種類でフィルタリングができます

処理時間でフィルタリングするとその時間以上かかったイベントが表示されるので問題ないか確認してみる


ビューの切り替え

⑤のところでビューを切り替えることができます

●時系列方式

FramesView1.png

●1フレームごとの積み上げ棒グラフ形式

FramesView2.png

30FPSのラインを超えている処理がないか確認する

どちらも特定のフレームを選択すると、フレーム内のサマリーが表示される

●簡易モード

FrameChartView1.png

イベントがツリー上に表示されます

●詳細表示モード

FlameChartView2.png

ネストされたイベントが下に伸びるように表示され、どの関数がどれぐらいの割合を占めているかわかりやすくなります

下の方にあるのはメインスレッド以外の処理

このモードでは下記の操作ができます

W:ズームイン

S:ズームアウト

AとDで選択範囲を左右に移動

また、画面中央左あたりにある赤いタグですが、処理時間が長すぎたり

イベントにパフォーマンス上の問題がある場合に表示されます


メモリ使用量の見方

Memory.png

赤枠で囲んだ部分がメモリ使用状況グラフ

MEMORYでは、メモリ使用量、イベントリスナー数、要素数が折れ線グラフで表示されます

色付きの□をクリックすると表示/非表示を切り替えられます


描画負荷の見方

Paint.png

⑧にチェックを入れた状態で計測するとPaintイベントにPaint Profilerタブが追加される

(フィルタでPaintだけにチェックを入れるとわかりやすい)

描画APIごとの所要時間や描画の進捗を時系列で確認できます

描画APIの呼び出しが多すぎないか確認してみる


スクリーンショット

Screenshot.png

タイムラインの時系列にそったスクリーンショットが表示される


計測結果の見方の簡単な説明

例えば、↓この例で言うと

Summary_Detail.png

①15ms以上かかってるイベントでフィルタリング

②処理に時間がかかってるイベント(ここではFunction Call)をクリックしてSummaryを見てみると、21.65msかかってる

③Aggregated Details(処理の累積時間)を見ると、sdk.jsのs.create.whenReadyで17.3msかかってる

っていう感じで、どこの処理でどれくらいかかってるかがわかります


Profilesパネル

CPUやメモリの情報を収集できます

Profilesパネル.png


記録方法

調べたい項目をチェックし、計測ボタンを押す


計測結果の見方


Collect JavaScript CPU Profile

Profiles__CPU.png

①ビューの切り替え

②関数を選択した後にクリックすると、その関数をルートにして表示

③関数単体のCPU使用率

④関数の中から呼び出している関数の時間も含めたCPU使用率

ビュー

・Chart:フレームチャートとして表示

・Heavy(Bottom Up):処理時間の大きいもの順で表示

・Tree(Top Down):実行された関数を読み出しと実行の関係に基づいた構造で表示

Chart表示にすると、↓のように視覚的に確認できる

Profiles__CPU_Chart.png

TimelineパネルのChartビューと同じようにWASDで操作できる


Take Heap Snapshot


Summaryビュー

オブジェクトが占めるメモリの総容量を、コンストラクタ名でグルーピングして一覧表示

Profiles_snapshot_summary.png

各項目の説明

・Constructor:コンストラクタ名。このコンストラクタで作られたオブジェクトを表している

・Distance:オブジェクトのルートからの距離。この値が大きいほど深い参照を保持していることになる

・Objects Count:オブジェクトの数

・Shallow Size:オブジェクト単体のメモリ使用量

・Retained Size:オブジェクトとオブジェクトが参照しているオブジェクトも含めたメモリ使用量

「@数字」はオブジェクトIDを意味する

なぜアドレスじゃないかと言うと、GCが行われるとオブジェクトが移動する(アドレスが変わる)ので

アドレスだと意味をなさないから

オブジェクトを展開して、その中の項目を選択すると

そのオブジェクトがどこから参照されてるかなど詳細を確認できる(Retainersのところ)

上記の例で言うと

Leakコンストラクタを参照しているのが「Retainers」に表示されているleakとselfのオブジェクト

さらに、leakはオブジェクトID、@156711、@156453のfunctionに参照されている

その関数はどれかと言うと、同じオレンジ枠で囲んだHTMLButtonElementに定義された関数

という感じに読み取れる

Retainersパネルに何も表示されていない場合は、どこからも参照されてないということなのでGCの対象


Comparisonビュー

スナップショットを複数取得すると、スナップショット間の差分を比較できます

Profiles_snapshot_comparison.png

各項目の説明

・# New:新規オブジェクト

・# Deleted:削除オブジェクト

・# Delta:差分カウント

・Alloc. Size:割り当てられたメモリサイズ

・Freed Size:解放されたメモリサイズ

・Size Delta:差分メモリサイズ

スナップショットを取得した時点で正しく増減しているか確認する


Containmentビュー

グローバルに存在するオブジェクトをツリー構造で表示

Profiles_snapshot_containment.png

↓こんなこともあるそうです

Heap Snapshotを使った時のsetIntervalの罠


Statisticsビュー

ヒープ領域全体に占めるJavaScriptの各データを円グラフで表示

Statistics.png


Record Heap Allocations

ヒープ領域のタイムライン

RecordHeapAllocations_timeline.png

見方はスナップショットと同様(Summaryビューにメモリのグラフが表示されるのが違う点)

色付きのバーは計測中に確保されたメモリ

グレーはGCによって解放されたメモリ


パフォーマンスの計測・改善


簡易診断

まずは細かい計測・改善をする前にAuditsを使った簡易診断を行ってみる

Audits.png

Runボタンをクリックすると診断開始

診断結果

Audits_診断結果.png


Network Utilizationについての診断解説

Combine external CSS

 読み込むCSSファイルの数が多いので、いくつかのファイルにまとめる提案

Combine external JavaScript

 読み込むJavaScriptファイルの数が多いので、いくつかのファイルにまとめる提案

Enable gzip compression

 gzipで圧縮し転送量を削減する提案

 圧縮可能なファイルの一覧が表示される

 ただし圧縮と展開によるオーバーヘッドが生じることを考慮すること

Leverage browser caching

 キャッシュが有効でなかったり、有効期限設定がないリソースの一覧が表示される

Leverage proxy caching

 キャッシュは有効でも、”Cache-Control: public”を指定していないリソースの一覧が表示される

Minimize cookie size

 このページにおけるクッキーのサイズを示し警告している

 サイズが小さい場合でもこの警告は出る

Parallelize downloads across hostnames

 複数のホストを使いリクエストを分散させると、並列ダウンロードでパフォーマンスが向上するというもの

Serve static content from a cookieless domain

 CSSや画像などクッキーを必要としないリソースの場合、クッキーレスの別ドメインにそれらのリソースファイルを配置することで

 その分クッキーサイズを小さくできるということ

Specify image dimensions

 img要素にwidthとheightが指定されていないと警告が出る

 それらを指定することで、ブラウザ処理に無駄がなくなりページ表示速度を改善できる


Web Page Performanceについての診断解説

Optimize the order of styles and scripts

 CSSファイルとスクリプトファイルの、記述順序を変えたほうが良いという提案

 スクリプトファイルはbodyの閉じタグの直前に置くといい

Remove unused CSS rules

 このページで使用していないCSSルールを列挙してくれる

 ただし別ページで使用しているルールもありますので、むやみに削除しない

Use normal CSS property names instead of vendor-prefixed ones

 ベンダープレフィックスなしの正式な構文があるのに、ベンダープレフィックス付きプロパティしか書かれていないという警告


ダウンロード編


調査方法

NetworkパネルでSizeカラムやTimeカラム、TimelineカラムのTotal Durationでソートしてみる

↓これはTimelineカラムのTotal Durationでソートした例

パフォーマンス_ダウンロード.png

棒グラフの薄い部分はLatency(ネットワーク接続開始からレスポンス最初の1バイトがブラウザに到達するまでの時間)

     濃い部分はContent Download(レスポンスの受信開始から完了までの時間)


改善方法

圧縮がかかっているかチェック

→テキストなら難読化、gzipなど。画像なら色数を減らす、サイズを小さくするなど


待機時間編

待機時間は静的なファイルを返却している場合は短い

動的なデータを返却している場合は、サーバで多くの処理が行われるため長くなる可能性がある


調査方法

NetworkパネルでTimelineカラムのLatencyでソートして、Waiting(TTFB)が長いものを調べる

パフォーマンス_待機時間.png


改善方法

サーバ処理を最適化する

または、リクエスト結果をブラウザストレージにキャッシュして、サーバへのリクエストを減らすのも有効


レンダリング(レイアウト算出)編


調査方法

TimelineパネルでFrames Viewの棒グラフで高いところを探す

サンプルソース

https://googlesamples.github.io/web-fundamentals/samples/tools/chrome-devtools/profile/rendering-tools/forcedsync.html

ForcedSynchronousLayout.png

黄色の警告マークのイベントを開くと、さらにLayoutで警告が出てるのがわかる

この場合、Forced synchronous layoutという現象が発生している

サンプルソースのようにレイアウト情報の参照と更新が交互に繰り替えされて「Forced synchronous layout 」が頻発する

Forced synchronous layoutはJavaScriptで要素のレイアウトに関するプロパティを参照した時に発生する

↓この辺も参考に

http://tokkono.cute.coocan.jp/blog/slow/index.php/web-technology/reflow-and-repaint-in-browser/


改善方法

参照と更新のタイミングをまとめてしまえばOK


レンダリング(スタイル評価)編

サンプルソース

http://image.gihyo.co.jp/assets/files/magazine/wdpress/2015/89/WDB89-toku1-DevTools.zip


調査方法

TimelineパネルでPaintingにかかってるフレームを探す

さらに特定のPaintイベントの中を覗いてペイント処理の詳細を調査

StylePeformance1.png

↑background-colorのみ

StylePeformance2.png

↑background(radial-gradient)、border-radius、box-shadow


改善方法

適用するスタイルの種類を変える(デザインと相談)


メモリ編


調査方法

①Take Heap Snapshotでスナップショットを複数取って比較してみる

例えば、下記コードを実行し比較してみると、Detached DOM treeというのが表示されます

window.detached = document.createElement('div');

document.documentElement.appendChild(detached);
document.documentElement.removeChild(detached);
for (var i = 0; i < 1000; i++) {
detached.appendChild(document.createElement('div'));
}

DetachedDOMtree.png

「Detached DOM tree」はDOMツリーに存在しないけど、存在しているDOM要素なので

メモリとしては残ってます

②TimelineパネルでMemoryを表示してみてイベントリスナ等の推移を確認してみる

HeapSnapshotListener.png

こんな感じで増え続けていないかチェック

③タスクマネージャ

Chromeにもタスクマネージャが用意されています

Shift+Escでウィンドウが開きます

タスクマネージャー.png

ウィンドウ上で右クリックすると項目メニューが表示されるので確認したい項目を選択する

JavaScriptメモリを選択してメモリが増え続けないかチェックしてみる


改善方法

①の場合は、detached = null;をfor文の後に入れると解放されます

window.detached = document.createElement('div');

document.documentElement.appendChild(detached);
document.documentElement.removeChild(detached);
for (var i = 0; i < 1000; i++) {
detached.appendChild(document.createElement('div'));
}
detached = null;

②の場合は、①と同様に不要になったら削除する

var button = document.getElementById('button');

button.addEventListner('click', function __click() {
button.removeEventListener('click', __click);
}, false);

リスナだけでなくタイマーも削除すること


参考文献

WEB+DB PRESS Vol.89の「[詳解]Chrome Developer Tools Web開発を加速する!」を参考にさせて頂きました