在移動互聯(lián)網(wǎng)時代,由于設備資源受限、網(wǎng)絡不穩(wěn)定等因素,Web 端和移動端的性能優(yōu)化顯得尤為重要,如果性能不好,用戶就容易流失,ToC 的產品尤為明顯,體驗差的產品必然會被市場淘汰。如何做好性能優(yōu)化是每個企業(yè)都會關注的。
在將于 11 月 24-25 日舉辦的 GMTC 全球大前端技術大會上,快手性能優(yōu)化負責人楊凱將會分享《快手 APM 平臺建設與性能優(yōu)化》。他表示,“隨著快手 App 功能越來越多,App 的性能也面臨著嚴峻的挑戰(zhàn),諸如 App 越來越卡、內存占用越來越大、包大小不斷增加等各類問題都嚴重影響著用戶體驗”。InfoQ 在會前采訪了楊老師,我們一起來看看快手是如何應對性能挑戰(zhàn)的。
InfoQ:快手 APM 指標監(jiān)控平臺的建設背景是什么?目前發(fā)展現(xiàn)狀如何?
楊凱:APM 是我們針對快手的性能檢測做的一個監(jiān)控平臺,其建立背景主要有兩方面 , 一方面有很多用戶反饋在使用我們的 App 時,遇到過卡、閃退和發(fā)熱等問題;另一方面,我們從現(xiàn)有數(shù)據(jù)分析得出結論:性能對于用戶活躍度有著重要影響。
目前,我們已經基本完成了崩潰、內存溢出(OOM)、應用無響應(ANR)、卡頓、啟動、幀率(FPS)、包大小的線上、線下監(jiān)控,電量、流量等監(jiān)控正在開發(fā)中。線上的話,我們有天級、小時級的監(jiān)控,并且重點指標有完善的報警機制。對于包大小、啟動等,我們建立了一套實驗室環(huán)境,可以監(jiān)控到每個 mr(Merge request ) 導致的變化,以及日常迭代劣化。
APM 主要解決我們面臨的各種穩(wěn)定性和性能問題。效果非常明顯:
優(yōu)化了 40% 的啟動速度,提升了我們的 0 播、留存等關鍵產品數(shù)據(jù)。優(yōu)化了 23M 包大小,大大降低了我們新用戶獲取成本。兩個優(yōu)化,都獲得了快手技術線的績效提升獎。
InfoQ:你們在搭建指標監(jiān)控過程中遇到哪些難點?是怎么解決的呢?
楊凱:APM 可以做的事情很多,容易鋪得很廣,但每一點都做不透。另一方面,雖然大家都覺得性能重要,但如果沒有數(shù)據(jù)的支撐,也容易陷入到處救火而看不到成績得窘境。所以我們一方面參考業(yè)內經驗、根據(jù)數(shù)據(jù)分析得出重要程度,評估每個方向的優(yōu)先級,各個擊破,抓住重點,力求每一點都做到極致,并且能從用戶數(shù)據(jù)上得到充分的體現(xiàn)。
我們是一個音視頻軟件,所以對內存的使用,尤其是 C++ 申請的內存會比較多。內存泄露,OOM、地址空間不足等問題非常突出。業(yè)內現(xiàn)有方案對我們來說并不完全適用,需要我們根據(jù)自己的實際情況,開發(fā)監(jiān)控、解決問題。業(yè)內有比較成熟的 malloc hook 方案監(jiān)控 C++ 內存的申請、釋放,但我們還需要知道哪些內存不可達,哪些大塊內存被長時間持有,才能更好地解決我們的問題。
另外,我們在線上發(fā)現(xiàn)的性能問題,通常不是我們性能組能獨立解決的,需要推動各業(yè)務方配合解決。這些工作會給大家日常的開發(fā)工作帶來額外的負擔,但它又對我們產品體驗有很重要的影響。所以我們一方面盡量完善工具,上報更多更全的數(shù)據(jù),另一方面我們要將大家作為一個整體團隊,享受項目的成果。
InfoQ:內存優(yōu)化過程中遇到的問題是如何解決的?
楊凱: 我們有一套自己的方法論,即定義問題、分析問題、解決問題、驗收以及防劣化。
定義問題: 在最開始階段,先將問題量化,例如用戶卡,就需要定義什么叫卡,怎么監(jiān)控用戶卡?
分析問題: 我們會在收集到足夠的用戶數(shù)據(jù)后,分析用戶實際的問題場景、原因。解決問題: 很多問題,并不是性能組可以解決的,但是我們可以提供工具定位、發(fā)現(xiàn)問題,推動相關開發(fā)人員解決。驗收: 對于一些重要的優(yōu)化,我們會通過 AB 上線驗收效果,回收技術數(shù)據(jù)和產品數(shù)據(jù)(例如優(yōu)化 FPS,我們會看相關頁面 CTR 的變化)。防劣化: 我們會制定一些機制、方案,防止已經優(yōu)化的數(shù)據(jù)在日常的迭代中劣化,包括每個版本灰度期間的監(jiān)控、線下實驗室環(huán)境測試、開發(fā)階段提醒等。
InfoQ:怎么進行內存優(yōu)化?
楊凱: 內存主要是 JAVA 和 C 兩部分,對于 Java,我們研發(fā)了一套線上裁剪、分析、上傳用戶本地鏡像的方案,可以做到用戶內存不足時,快速而準確地上報當時的 Java 內存狀態(tài),初步可以判斷出來泄露和內存大戶是誰。上報后我們會對所有用戶的信息做匯總、展示。Java 部分的內存監(jiān)控主要做了以下幾點:
內存鏡像轉儲,我們研發(fā)了一種高效 dump 方案,解決了傳統(tǒng)方法虛擬機內存轉儲需要暫停虛擬機的問題。內存鏡像分析,研發(fā)了基于 shark 的低內存開銷、低 CPU 開銷的獨立進程解析方案,采用了更為節(jié)省內存的高性能數(shù)據(jù)結構以及更為高效的內存索引,增加了同類型對象閾值用于 GC Root 最短路徑搜索剪枝,可以在手機側 10 分鐘內完成 400M 鏡像、200 萬 對象的極端 case 解析。內存鏡像裁剪,我們研發(fā)了一種 hook 虛擬機內存鏡像轉儲時 IO 的高效裁剪方案,解決了傳統(tǒng)裁剪效率低、成功率低的問題,輔以 zstd 壓縮,90% 內存鏡像可以壓縮至 80M 內。
C 的內存 我們主要利用編譯器插樁及 malloc hook 記錄所有活著的內存塊,利用 mark-and-sweep 算法在單獨的進程中分析測試應用進程 Native Heap 中不可達的內存塊。將發(fā)現(xiàn)的不可達內存上報后臺。具體操作如下:
利用編譯器插樁及 malloc hook 記錄所有活著的內存塊(包含內存塊地址、backtrace 信息),對性能影響較小。利用 mark-and-sweep 算法在單獨的進程中分析測試應用進程 Native Heap 中不可達的內存塊(包含內存塊地址)。對于步驟 2 中收集到的不可達內存塊,從 1 中獲取其對應的 backtrace 信息,將泄漏信息上報至 APM 監(jiān)控平臺。APM 監(jiān)控平臺解析泄漏信息(backtrace 信息符號化等),做友好的展示,業(yè)務方根據(jù) APM 展示信息可快速定位泄漏問題。
InfoQ:如何優(yōu)化卡頓?
楊凱: 我們定義的卡頓是:一個消息 / 任務在主線程執(zhí)行超過 1s。
優(yōu)化主要看卡頓的堆棧特征、當前 CPU 占用、其它線程正在執(zhí)行的任務。通常有以下幾種情況:CPU 占用過高(一般是主線程或者子線程任務重),主線程等鎖(需要看其它線程當時的任務),系統(tǒng)服務忙(binder 調用耗時長)。解決方案一般需要結合場景、頁面,增加 log 等,豐富更多上報信息,定位主要問題,或者緩解 CPU 占用。
InfoQ:在啟動優(yōu)化這部分做了哪些動作,優(yōu)化前后對比效果如何?
楊凱: 啟動優(yōu)化我們主要是建立啟動框架,將啟動所有的任務,全部收斂到框架內,統(tǒng)計每個任務的耗時、相互依賴關系。
啟動優(yōu)化定位問題:
將啟動時運行的代碼,按照功能,做成 task;線上收集每個 task 的耗時;在線下,在 Android 端利用 systrace、在 IOS 端利用自研的火焰圖工具,來分析耗時。
優(yōu)化手段:
優(yōu)化整體流程;分場景、分用戶特性,推遲甚至取消一些 task (根據(jù)用戶登錄狀態(tài),用戶使用習慣,后面我們會用機器學習預測 task 是否需要初始化);特別關注一些鎖的等待、主線程 CPU 分配不足等問題;一些系統(tǒng) API,背后會引發(fā)一系列的初始化(setcookie,會引起 WebView 內核初始化);主動 dex2oat;二進制重排、dex 重排。
另外,針對線上收集到的信息,重點優(yōu)化耗時較多的任務。線下通過 systrace 等工具,定位、驗證修復。然后依據(jù)用戶的不同特征,初始化不同任務。例如,未登錄用戶不需要初始化拍攝相關任務。我們一期優(yōu)化效果達到了 40%,后續(xù)幾期優(yōu)化,也收到了不錯的效果。用戶側收益明顯,用戶(尤其是新用戶)0 展示 、0 滑、 0 播放,以及留存等數(shù)據(jù)都有非常大的提升。