寫在前面
本文第一部分翻譯自 Chrome DevTools 文檔:How to Use the Timeline Tool
去年也做過這個事情,見 Chrome DevTools,Chrome 版本和文檔都在持續更新中,Timeline 面板功能更強大了,佈局也發生了一些變化
一。譯文
用 Chrome DevTools 的 Timeline 面板可以記錄分析應用運行時的所有活動,可以用來調查應用的性能問題
[caption id="attachment_1260" align="alignnone" width="625"]
timeline-panel[/caption]
內容摘要
-
生成時間軸記錄來分析頁面載入之後或者用戶交互之後發生的所有事件
-
在概覽窗格中查看 FPS、CPU 和網絡請求
-
點擊火焰圖(flame chart)中的每個事件可以查看其詳細信息
-
可以放大記錄中的一部分以便分析
Timeline 面板概述
[caption id="attachment_1263" align="alignnone" width="938"]
timeline-annotated[/caption]
Timeline 面板從上到下依次為:
- 控制選項
開始記錄、停止記錄、配置記錄過程中抓取哪些信息
-
概覽
對頁面性能的高度總結,更多內容在下面
-
火焰圖
CPU 堆棧跟蹤的可視化表示
在火焰圖上可能會看到 3 條豎直虛線,藍色線表示 DOMContentLoaded 事件,綠線表示第一次繪製的時間點,紅色表示 load 事件
- 詳細信息
選中一個事件後,最下方的詳細信息窗格會展示該事件的詳細信息。沒選中事件的時候就展示選定時間範圍的綜合信息
概覽窗格
[caption id="attachment_1265" align="alignnone" width="954"]
overview-annotated[/caption]
包括 3 種圖:
-
FPS
每秒幀數。綠條越高,FPS 越高。FPS 圖上方的紅塊表示長幀,很可能會卡頓(jank)
-
CPU
CPU 資源。面積圖(area chart)表示哪種類型的事件消耗了 CPU 資源
-
NET
每個色條代表一個資源,條越長,獲取資源需要的時間越長。每個條上淺色的部分表示等待時間(從請求資源到拿到第一個字節的時間)。深色部分表示數據傳輸的時間(從拿到第一個字節到拿完最後一個字節的時間)
條的顏色含義如下:
HTML:藍色 Scripts:黃色 CSS:紫色 Media:綠色 其它亂七八糟的資源:灰色
生成記錄
打開 Timeline 面板,打開想要記錄的頁面,然後 reload 頁面。Timeline 面板會自動記錄 reload 過程
要記錄頁面交互的話,先打開 Timeline 面板,然後點擊記錄按鈕或者按下快捷鍵 Cmd+E(Mac) 或者 Ctrl+E(Windows/Linux)。記錄按鈕變紅表示正在記錄。進行頁面交互,然後再次點擊記錄按鈕或者按下鍵盤快捷鍵停止記錄
記錄結束時,DevTools 會猜測哪部分是你最關心的,並自動把那部分選中
溫馨提示:
-
讓記錄過程盡可能短。越短越容易分析
-
避免不必要的動作。避免與想要記錄分析部分不相干的外部動作(鼠標點擊,網絡加載等等)。例如,想要記錄發生在點擊登錄按鈕之後的事件,不要同時滾動頁面,加載圖片等等
-
禁掉瀏覽器緩存。記錄網絡操作時,最好通過 DevTools 設置面板或者網絡情況(Network conditions)選項禁掉瀏覽器緩存
-
禁掉插件。Chrome 插件會影響應用的 Timeline 記錄,可以在隱身模式(incognito mode)打開 Chrome 窗口,或者創建一份新的 Chrome 用戶配置(Chrome user profile)來保證環境沒有任何插件
查看記錄詳情
[caption id="attachment_1269" align="alignnone" width="936"]
details-pane[/caption]
在火焰圖中選中一個事件後,詳細信息窗格會顯示關於事件的額外信息
一些選項卡在所有事件類型中都有,比如 Summary,另一些選項卡只在某些事件類型中才有,關於記錄類型的詳細信息,見 Timeline event reference
在記錄過程中截屏
[caption id="attachment_1270" align="alignnone" width="625"]
timeline-filmstrip[/caption]
Timeline 面板能在在頁面載入過程值截屏,這種特性就像電影膠片一樣
在開始記錄之前,先在控制窗格勾選 Screenshots 複選框,就會記錄截屏,屏幕截圖顯示在概覽窗格下方
鼠標懸停在屏幕截圖,或者概覽窗格上,可以查看該時間點放大的屏幕截圖。把鼠標從左向右移動可以模擬動畫效果
JS 性能分析
[caption id="attachment_1271" align="alignnone" width="625"]
js-profile[/caption]
在開始記錄之前,勾選 JS Profile 複選框可以捕獲時間軸記錄中的 JS 調用棧。在開啟 JS Profile 時,火焰圖會展示每個被調用的 JS 函數
繪製性能分析
[caption id="attachment_1272" align="alignnone" width="625"]
paint-profiler[/caption]
在開始記錄之前,勾選 Paint 複選框可以深入查看 Paint 事件。開啟繪製性能分析後,點擊一個 Paint 事件時,會在詳細信息窗格顯示出 Paint Profiler 選項卡,展示更細粒度的事件信息
渲染設置
[caption id="attachment_1273" align="alignnone" width="822"]
rendering-settings[/caption]
打開主 DevTools 菜單並選擇 More tools > Rendering settings 進行渲染設置,可能有助於調試繪製問題。渲染設置打開之後是一個選項卡,挨在 Console 選項卡旁邊(如果沒有的話,按 esc 顯示)
查找記錄
[caption id="attachment_1274" align="alignnone" width="690"]
find-toolbar[/caption]
在查看事件時,可能只想關注一種類型的事件。例如,可能需要查看每個 Parse HTML 事件的詳細信息
在 Timeline 面板按下 Cmd+F(Mac) 或者 Ctrl+F(Windows/Linux) 打開查找工具條,輸入想要查看的事件類型名,例如 Event
工具條只對當前選中的時間幀有效,選中時間幀之外的所有事件都不會出現在查找結果中
通過上下箭頭可以在結果中按時間順序移動。所以第一條結果是選定時間段中最早的事件,最後一條是最晚的。每次點擊上下箭頭時,都會選中一個新的事件,所以可以在詳細信息窗格里查看其詳細信息。點擊上下箭頭等價於點擊火焰圖中的事件
放大一段時間軸
[caption id="attachment_1275" align="alignnone" width="625"]
zoom[/caption]
可以把記錄的一部分放大,這樣更容易分析。可以用概覽窗格來放大記錄的一部分,縮放之後,火焰圖會自動縮放到與這部分對應
想要放大時間軸的一部分的話:
-
要在概覽窗格,用鼠標拖選出時間軸的一部分
-
調整標尺區域的灰色滑塊
選定一部分之後,就可以用 WASD 調整選區。W 和 S 是放大縮小,A 和 D 是左右移動
導出導入記錄
[caption id="attachment_1276" align="alignnone" width="517"]
save-open[/caption]
可以在概覽或者火焰圖窗格中通過右鍵點擊來保存和打開,以及選擇相關選項
二。動畫性能指標
幀率
最直觀的是幀率(單位是 FPS,表示每秒幀數),30 以下人眼能感受到明顯卡頓,60 以上眼睛就看不出來區別了,30-60 基本流暢
性能目標是讓動畫幀率盡量到達 60FPS,這樣就不會有一卡一卡的感覺,所以動畫庫一般是這樣做的:
/* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */
var rAFShim = (function() {
var timeLast = 0;
return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
var timeCurrent = (new Date()).getTime(),
timeDelta;
/* Dynamically set delay on a per-tick basis to match 60fps. */
/* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */
timeDelta = Math.max(0, 16 - (timeCurrent - timeLast));
timeLast = timeCurrent + timeDelta;
return setTimeout(function() {
callback(timeCurrent + timeDelta);
}, timeDelta);
};
})();
支持 requestAnimationFrame 的話直接用,讓瀏覽器決定執行頻率(一般大於 60 次每秒),不支持的話降級方案是 setTimeout:
// 16 = 1000 / 60
timeDelta = Math.max(0, 16 - (timeCurrent - timeLast));
要達到每秒 60 次的話,執行間隔不能超過 16ms,所以 setTimeout 延遲時間一般為 16 - 函數執行所需時間,如果函數執行時間超過 16ms,延遲為 0
幀率的影響因素很多,比如計算密集的 JS 任務佔用了大片 CPU 時間導致動畫相關 JS 延後執行、佈局變動導致頻繁重繪 Rendering/Painting 時耗變大、複合層太大太多導致 Composite Layers 時耗增大和 GPU 繪製壓力變大時耗變長等等
內存
本來動畫與內存沒直接關係,但因為硬件加速,就有了 動畫元素應用硬件加速->創建複合層->消耗內存 的關係
因為 CPU 與 GPU 不共享內存,所以創建複合層需要把元素渲染後對應的位圖數據打包發送給 GPU,這份數據就是「動畫消耗的內存」,所以動畫卡頓,可能是自己作的,內存也是動畫性能指標之一
對應到動畫上,就是複合層數量與複合層大小,複合層越多越大,位圖數據就越大,消耗內存也越大
三。動畫性能調試方法
針對幀率和內存兩方面,有一些調試方法:
###1. 什麼東西最耗時間
用 Timeline 工具記錄感覺比較卡頓的一段,選中 FPS 欄中有紅條的部分,看最下方詳細信息窗格中的 Summary,從餅圖找出最耗時的東西,例如:
Range: 294ms - 1.17s
Total: 878.74?ms
29.1?ms Loading
197.0?ms Scripting
43.0?ms Rendering
4.4?ms Painting
116.3?ms Other
489.0?ms Idle
其中 JS 耗時最多,那麼點擊 Event Log 選項卡,按時耗排序,展開前幾項,定位到具體 JS 文件,再分析耗時原因,考慮優化方案
如果是 Rendering 耗時最多,那麼應該考慮精簡 HTML 結構,去掉不必要的元素,簡化佈局以及減少 reflow;如果是 Painting 那麼就應該關注複合層,是不是複合層太多太大,考慮去掉多餘的、不可見的複合層,以及增大動畫元素的 z-index,避免隱式創建複合層
P.S. 建議先開啟隱身模式,再用 Timeline 記錄,否則各種插件的 JS 會干擾原因定位(餅圖不準確)
###2. 什麼東西最耗內存
觀察記錄時間段的內存圖,選中峰值部分,在 Event Log 選項卡中找出相關的事件,考慮事件相關的 JS
但一般來說 JS 的影響比複合層影響要小很多,可以直接查看複合層的情況:在概覽窗格中拖選出 FPS 有紅條的時間區間,查看詳細信息窗格的 Layers 選項卡,展開 document 下的各層,關注幾件事情:
-
複合層的數量是否符合預期,有沒有不應該出現的層?
-
點擊層可以查看其大小和內存消耗以及該層的創建原因,看內存消耗是不是太大?
大尺寸的複合層非常耗內存,例如尺寸為 375x667 的層消耗內存 1.5MB,如果這種大尺寸的層比較多,會佔用大量的內存,內存吃緊肯定會卡頓
而 31x31 的小尺寸按鈕也需要 16KB,所以除了關注大尺寸動畫元素外,複合層的數量也是一個重要因素,比如泡泡,煙霧等需要大量小元素才能實現的動畫,考慮效果降級或者減少元素數量,利用偽元素以及 border、outline、box-shdow 等屬性簡化複雜的 HTML 結構
P.S. 關於減少複合層,降低內存消耗的更多方法,請查看 [CSS 動畫與 GPU-性能優化技巧](/articles/css 動畫與 gpu/#articleHeader10)
暫無評論,快來發表你的看法吧