在瀑布下用火焰烤餅:三步法助你快速定位網站效能問題(超詳細)

kagol發表於2020-12-13

DevUI是一支兼具設計視角和工程視角的團隊,服務於華為雲DevCloud平臺和華為內部數箇中後臺系統,服務於設計師和前端工程師。

官方網站:devui.design

Ng元件庫:ng-devui(歡迎Star)

官方交流:新增DevUI小助手(devui-official)

DevUIHelper外掛:DevUIHelper-LSP(歡迎Star)

引言

效能,是一個問題。

每個專案成長到一定的規模,都幾乎必然要遇到效能問題,當遇到效能問題時,我們是:

一臉懵逼,就知道很卡、很慢,不知道為什麼

還是

能夠快速洞察效能瓶頸,找到行之有效的優化方案

取決於我們對效能的理解深淺,以及是否有一套好的工具和方法。

接下來給大家分享我自己在定位業務效能問題時常用的三步法,為了方便記憶,我把它總結為一句話:

在瀑布下用火焰烤餅

話不多說,喝口水直接開擼!

Performance皮膚簡介

介紹三步法之前,先來簡單瞭解下Chrome開發者工具的Performance效能皮膚,以及效能分析報告的基本組成。

生成效能分析報告

以DevUI團隊的掘金個人主頁為例,使用Chrome瀏覽器訪問:https://juejin.cn/user/712139267650141

然後按F12開啟Chrome的開發者工具,選擇Performance效能皮膚。

這時我們會看到一個簡單的指引:

指引裡面有兩個按鈕,上面的按鈕是手動錄製,下面的是自動錄製,我們點選傻瓜式的自動錄製,自動錄製會自動重新整理頁面,在頁面載入完成之後,生成該頁面的效能分析報告,無需人工干預,非常方便。

等個幾秒鐘報告就生成好了,一眼看去,花花綠綠的,不知道從何看起?

效能報告的組成

我們對生成的效能分析報告做一個簡單的皮膚分類,看起來就很清晰了。

工具欄

效能報告的頂部是一個工具欄(或者叫控制皮膚),裡面有一堆按鈕,我這邊用得比較多的是前面三個,其中前兩個在指引裡已經介紹過了,第三個是用來清除報告的。

還有兩個隱藏的功能也很有用,一個是模擬慢網速的,另一個是模擬慢CPU的,對移動端應用做效能優化可能會用到。

概覽皮膚

工具欄下面是一個概覽皮膚,顯示了整個頁面載入過程中的FPS(Frames Per Second,每秒傳輸幀數),用來評估頁面的流暢度,有大片紅色說明頁面可能存在卡頓。

FPS下面是CPU處理各個任務花費的時間,再往下是網路請求的耗時,概覽皮膚最下面是每一幀的截圖。

執行緒皮膚

概覽皮膚往下是執行緒皮膚,預設展開的是網路請求瀑布圖,其他執行緒的詳情都是收起的。

每個執行緒皮膚對效能分析都有價值,而我最常用的是瀑布圖和火焰圖,後面會重點分析這兩個圖,如何利用這兩張圖來分析網站的效能瓶頸。

記憶體皮膚

再往下是記憶體皮膚,記憶體皮膚需要在控制皮膚中手動開啟,它是一個分類的記憶體佔用折線圖。

每條折線是一種任務隨時間推移的記憶體佔用:

  • JS堆疊
  • 文件
  • HTML節點
  • 事件監聽
  • GPU記憶體

詳情皮膚

最下面是詳情皮膚,首先看到的是一個餅圖,這個餅圖顯示了各種型別任務的佔比,這個非常有用,能否一眼看出什麼型別的任務是效能瓶頸。

是資源載入還是指令碼執行?是頁面渲染還是影像繪製?又或者是空閒時間太長?

第一步:看餅圖

剛才介紹Performance皮膚的組成時,提到了3個非常有用的效能分析利器,分別是詳情餅圖請求瀑布圖主執行緒火焰圖

我把這三張圖總結成一句話:

在瀑布下用火焰烤餅

這句話也是我自己在做效能分析和優化時,屢試不爽的小技巧。

詳情皮膚中的餅圖用於展示各種型別任務的耗時佔比。

主要有以下幾種任務:

  • 藍色是資源載入
  • 黃色是指令碼執行
  • 紫色是頁面渲染
  • 綠色是圖形繪製
  • 白色是空閒時間

還是舉剛才的例子。

從餅圖可以看出佔比最多的是指令碼執行空閒

指令碼執行時間長,我們大概可以猜測裡面可能存在長任務(Long task);

而空閒佔比多可能是等待伺服器的響應時間太長。

餅圖可以快速形成基本的判斷,而具體原因則需要分析瀑布圖和火焰圖。

第二步:看瀑布圖

我們來看下請求瀑布圖,瀑布圖和火焰圖都是執行緒皮膚的一部分,瀑布圖的橫軸是時間軸,瀑布圖上有很多五顏六色的色塊,這些色塊就是請求塊,每種顏色代表一類資源:

  • 藍色是HTML檔案
  • 紫色是CSS檔案
  • 黃色是JavaScript檔案
  • 綠色是圖片
  • 灰色是後臺介面

我們主要關注那些長色塊,長色塊意味著耗時長,可能是效能瓶頸。

還是看下掘金個人主頁的瀑布圖。

總結瀑布圖的特點

我們先觀察這張圖有什麼特點,圖形觀察能力,相信大家小學就已經培養起來了,大致我們可以總結出以下比較明顯的特點:

  • 特點一:大瀑布被分成三個小瀑布
  • 特點二:最左邊的小瀑布大部分都是黃色色塊,中間的小瀑布大部分是灰色色塊,最右邊的小瀑布大部分是綠色色塊
  • 特點三:前兩個瀑布之間有一段間距,中間什麼色塊都沒有
  • 特點四:後兩個瀑布被一個灰色色塊的“尾巴”連在了一起
  • 特點五:頂部有一個超長的灰色色塊

類似的特點我們還可以總結出很多來,但是這些特點說明了什麼呢?能否幫助我們定位效能瓶頸呢?

回答這些問題需要我們對瀑布圖以及瀏覽器原理有很多的認識,我們一步步來分析吧。

分析瀑布圖的含義

我們按從左到右,從上到下的順序進行分析,最左邊有兩個色塊,一個灰色色塊,一個藍色色塊,我們分別點選這兩個色塊,在詳情皮膚看下它們的詳情資訊。

先看灰色色塊

我們有注意到這個請求的啟動器(Initiator)是一個Chrome外掛:chrome://new-tab-page/omnibox.mojom-lite.js

因此我們不關注,接著看藍色色塊

前面我們已經介紹了,藍色色塊代表HTML檔案,我們從詳情的Mime Typetext/html也可以驗證這一點。

我們滾動滑鼠滾輪,把這個瀑布圖放大,看這個藍色請求塊的細節

請求塊的組成

通過檢視細節圖,我們有了新的發現:

每個請求塊都由四部分組成:

  1. 左側線:代表請求傳送之前的時間(Before Request Sent)
  2. 淺色塊:代表請求已經傳送(Request Sent),直到伺服器返回第一個位元組給瀏覽器(TTFB, Time to First Byte)
  3. 深色塊:伺服器返回的內容全部下載到瀏覽器(Content Download)
  4. 右側線:等待主執行緒處理(Waiting for main thread)

這個HTML檔案是整個網頁渲染的起點,成功請求並下載這個檔案,才會有接下來的故事。

這個請求塊的淺色塊部分佔比比較大,根據前面的介紹,淺色部分代表的是伺服器的響應速度,瀏覽器已經早早地發出了請求,伺服器卻遲遲才給回應(第一個位元組到達瀏覽器)。

中間可能是網路慢,也可能是伺服器處理速度慢,需要具體排查,畢竟這個HTML檔案不算大,才111KBb,卻花了179ms。

對比另外一個檔案layouts.default.js,體積比它大124KB,請求耗時卻比它小一半多,才74ms。(後來發現這個資料不穩定,這個HTML檔案應該不至於構成效能瓶頸)

另外所有後續的請求都依賴於這個HTML,沒有它其他請求都不會發生,它是一個阻塞請求,效能必須要有保障。

發現可能的效能瓶頸

我們繼續看右邊的請求塊,頂部那個超長的灰色塊依然是Chrome外掛的請求,我們不管,看下面那一堆黃色的請求塊,這些都是JavaScript檔案。

HTML檔案下載完了之後,就會開始一行一行解析其中的HTML標籤,遇到設定了誰、src屬性的<script>標籤,就會去下載src指定的JavaScript指令碼檔案。

從瀑布圖可以看出,一共並行下載了8個JavaScript檔案,它們的域名都是一樣的:sf1-scmcdn2-tos.pstatp.com

不是說Chrome瀏覽器對同一個域名,並行的請求數最大是6個嗎?

不僅僅是JavaScript檔案,下面還有3個同域名的圖片資源,也是在並行請求的,也就是說幾乎同時發起了11個請求。

這說明

掘金的靜態資源伺服器升級到了HTTP/2

HTTP/2的多路複用可以實現一個TCP連線同時傳輸多個資源。

我們到Network皮膚裡去看下這些JavaScript的請求詳情,果然和我們猜測的一致,這一點必須給掘金點個贊?

發一個某86網站和掘金的對比圖,大家感受一下

某86網站:

掘金:

雖然前者更像一個瀑布,但是我喜歡後者絲滑般的體驗。

我們再來仔細看這8個請求,相信細緻的你一定發現了一個現象:

  1. 它們的共同點除了剛才提到的域名一樣外,這些請求塊的左右線都很短
  2. 有三個特別長的請求塊,分別是1/5/8,需要格外關注

請求塊的左右線都非常短是一個好現象,說明沒有什麼等待時間,所有時間都用在了傳輸資料上。

我們分別點選1/5/8請求塊看它們的詳情

請求塊 詳情
1 大小:4KB
耗時:635ms
5 大小:90KB
耗時:635ms
8 大小:3.9MB
耗時:633ms

這非常奇怪,1/5的資源大小和8的不在一個量級上,耗時卻比8還多。

為了確定這是偶然的,還是必然的,我又錄製了兩次這個掘金個人主頁的效能報告

這次和預期的基本一致,8耗時比其他都長,這個JavaScript檔案3.9MB,太大了,很可能是效能瓶頸。

其他

讓我們繼續往下分析,黃色JavaScript色塊下面一共有三種顏色的色塊:

  • 紫色:CSS樣式檔案
  • 綠色:圖片檔案
  • 灰色:字型檔案(大小為189KB)

這幾個檔案體積都不大,並且通過多次生成效能報告,發現這幾個請求耗時都不如第8個JavaScript檔案長,所以初步判斷這些請求不構成效能瓶頸。

接著看中間那個瀑布,通過多次生成效能報告,發現中間瀑布並沒有什麼特別耗時的請求,不過不管生成多少次報告,有一點是確定的,就是

這三個瀑布之間總是有些空白

這些空白到底說明了什麼呢?

看完火焰圖,相信你就會豁然開朗。

第三步:看火焰圖

在看正式的火焰圖之前,先來看一個瀑布圖和火焰圖放在一起的效果

看完這張瀑布和火焰的對比圖,你一定看出了一個現象

瀑布圖有空白的地方,火焰圖就有顏色;

瀑布圖有顏色的地方,火焰圖就是空白。

But Why?

要回答這個問題需要了解瀏覽器主執行緒執行任務的原理,以及火焰圖是做什麼的,彆著急,讓我們一步步來分析。

火焰圖是什麼

火焰圖也是執行緒皮膚的一部分,它代表的是瀏覽器主執行緒的任務流:

隨著頁面的載入,時間的推移,主執行緒依次做了什麼事兒

火焰圖的橫軸是時間,縱軸是一個個的巨集任務。

每個巨集任務下面若干個微任務,每個微任務下面有可能有很多子任務,依次類推。

由於有些任務的巢狀層級深,有些巢狀層級淺,所以呈現倒立的火焰狀。

每種型別的任務顏色都不一樣(無需記憶,有個大致的印象即可):

  • 解析HTML Parse HTML:藍色
  • 解析樣式 Parse Stylesheet:藍色
  • 評估指令碼 Evaluate Script:黃色
  • 重新計算樣式 Recalculate Style:深紫色
  • 繪製 Paint:深綠色
  • 執行微任務 Microtasks:黃色
  • Ajax請求 XHR Load:黃色
  • 函式呼叫 Function Call:黃色
  • 觸發定時器 Timer Fired:黃色

還是先大概看下掘金個人主頁的火焰圖

總結火焰圖的特點

然後用我們小學就學會的看圖找規律的技能,找到這個圖有什麼特點,大致掃一眼,我們就能總結出至少以下幾個特點:

  • 特點一:總的來看兩邊是空白,中間有三個大火焰
  • 特點二:兩邊的兩個大火焰正好對應瀑布圖的兩個空白(這就解釋了為什麼瀑布圖的三個小瀑布之間有空白)
  • 特點三:有些巨集任務特別長,並且背景色是紅色的陰影線(而不是灰色)、右上角有一個紅色的小三角形

多花點時間,可能我們還能有更多的發現,不過這幾個是最顯而易見的。

為了回答這些問題,我們需要近距離觀察下火焰圖。

分析火焰圖的含義

既然火焰圖代表主執行緒每個時間點都在幹嘛,那麼空白自然就意味著主執行緒沒在幹活,那麼,它在幹嘛呢?

它在等待

等待什麼呢?

等待伺服器返回一些必要的資源和資料

所以

火焰圖的空白處都是瀏覽器在等待伺服器返回資料

尋找長任務

在所有主執行緒執行的任務中,我們尤其需要關注的是那些耗時特別長的長任務(Long task),這些長任務的特點前面已經說了:

背景色是紅色的陰影線

右上角有一個紅色的小三角形

三個長任務1s鍾就找到了

分析長任務

接下來是分析長任務,找到耗時長的具體模組/元件/方法。

我們把最右邊最大的那個火焰放大,看看裡面到底有些什麼祕密。

放大之後,我們很快就發現這個耗時591ms的長任務,有90%的時間都花費在了一個叫init的方法上,這個方法一共執行了6次,其中3/4/6耗時尤其長

第n個init方法 詳情
3 耗時:197ms
4 耗時:93ms
6 耗時:111ms

這個init方法到底是做什麼的呢?

可能是掛在Vue元件的,會不會是有些元件特別大,裡面的邏輯太複雜,這裡需要掘金的前端給出答案。

再看下左邊那個第二大的火焰,同樣滾動滑鼠滾輪把它放大

我們發現其中有一個forEach迴圈特別耗時,這個迴圈好像在計算什麼東西,一共花了150ms。

這個依然需要看下具體的原始碼才能找到問題的根因。

通過火焰圖發現效能瓶頸的案例

最後給大家分享下我自己之前在XBoard看板專案中,通過火焰圖發現一個依賴庫的效能問題。

也是遵循一樣的思路:

  1. 找到長任務
  2. 將長任務的火焰圖放大
  3. 一層層往下找,直到找到一個耗時長的有名字的方法(現網大部分程式碼被壓縮混淆了,看不出名字,開發環境會更方便定位到存在效能問題的方法)
  4. 在火焰圖中點選這個方法,看詳情皮膚中Function後的連結,點選這個連結,直接跳轉到相應檔案中的指定方法中
  5. 在原始碼中搜尋這個方法名字,找到它
  6. 尋找解決方案

當時XBoard看板頁有一堆長任務,我找了其中的TOP3

然後將第一個長任務放大,很快就有了收穫,我發現其中有一個叫drawQrCode的方法耗時比較長,一共花了192ms。

接著通過檢視詳情,發現這是一個依賴庫的方法,該依賴庫定義了一個drawQrCode用來繪製二維碼,而這個二維碼其實不在看板頁面上,而是需要通過滑鼠hover到某個按鈕上才載入出來。

所以當時解決的方案就是延遲drawQrCode方法的執行,即:

首頁載入時,不執行drawQrCode方法,當滑鼠移到相應按鈕上時,才執行。

瀑布圖和火焰圖的關係

瀑布圖和火焰圖是相互補充、相互驗證的關係。

瀑布圖代表瀏覽器發起向伺服器的請求,然後瀏覽器根據伺服器返回的資料,通過指令碼執行相應的邏輯和頁面的渲染。

當瀑布圖有請求塊時,說明瀏覽器在向伺服器請求資料,如果瀏覽器必須依賴這些資料來做下一步的頁面渲染,那麼在伺服器返回資料之前,很可能瀏覽器就沒事幹,然後火焰圖上出現空白,餅圖也會出現空閒(Idle)。

當瀏覽器拿到伺服器返回的資料時,主執行緒正在處理這些資料,並渲染頁面,因此很可能就沒法向伺服器發請求,這時瀑布圖就會出現空白。

所以

  1. 發現瀑布圖出現空白,很可能存在長任務,需要找到具體的耗時方法,並進行優化
  2. 發現火焰圖出現空白,很可能是某些後臺介面慢或者存在超大靜態資源,需要定位到慢的原因,並想辦法優化

小結

本文先給大家簡單介紹瞭如何生成網站的效能分析報告,以及這份報告的大致組成;

接著跟大家分享我自己在定位業務效能問題時,經常使用的三步法:在瀑布下用火焰烤餅;

從餅圖中我們可以對網站的效能有一個大致的認識,從瀑布圖快速地發現慢介面和大資源,而從火焰圖中,我們可以細緻地洞察到具體哪個模組/哪個元件/哪個方法可能成為效能瓶頸。

最後給大家推薦Google官方的效能評估指南:
https://developers.google.com/web/tools/chrome-devtools/evaluate-performance

加入我們

我們是DevUI團隊,歡迎來這裡和我們一起打造優雅高效的人機設計/研發體系。招聘郵箱:muyang2@huawei.com。

文/DevUI Kagol

往期文章推薦

《大廠是如何用DevCloud流水線實現自動化部署Web應用的?》

《手把手教你使用Rollup打包?併發布自己的工具庫?》

《前端有了這兩樣神器,再也不用追著後臺要介面啦》

相關文章