天天看點

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

作者|手淘使用者體驗提升項目組

出品|阿裡巴巴新零售淘系技術部

Weex 化的店鋪性能優

▐  背景

店鋪業務做為商家營運、營銷的主陣地,是動态化方案激進的跟進者之一。2015年上線 Weapp 技術,就是在一碼多端上的嘗試。2017年,WeappPlus 的項目斐然崛起也和店鋪有着千絲萬縷的聯系。

沒看錯,就是 Weex 的第一個項目名。之後 Weex 如一陣飓風般刮過集團各大業務,或整體、或單頁、或卡片的應用場景層出不窮。店鋪作為第一批上線 Weex 的業務,承擔積極的推動作用,甚至一度把用戶端給做沒了,用戶端僅保留了店鋪路由的邏輯。在頁面渲染和呈現上,全業務、全頁面都由 Weex 來承載。

但是衆所周知,Weex 技術在帶來種種優點的同時,也有一個無法避免的短闆。頁面進入速度會變慢(相對于純用戶端頁面而言),特别是店鋪這種既需要複雜互動,同時還有三方 ISV 開放能力,單個頁面的 js 超過 500K 的複雜業務場景。

在結構上,店鋪的分為 

架構

 - 

子頁

 兩部分:

架構包含店鋪名,心鑽冠等級,頂部 tab 條,底部 tabbar 導航的能力。子頁就是各個 tab 切換後的中間區域。

首頁就是螢幕中央的主要業務區域,可以通過 tab 完成多個頁面間的切換。

架構 和 子頁,通過 embed 标簽完成嵌套關系。

同時為了讓使用者獲得更大的浏覽區域,引入了 Nested-Scroll, BindingX 等能力實作嵌套滾動,底色漸變等能力。

最後店鋪外投的 URL 形式衆多,需要有能力支援各式各樣的入口位址。

綜上,整個進店流程從鍊路上說可以分為三個階段:路由,架構,首頁。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

接下來,将從設計、分析、實作、上線等角度闡述整個優化工作的具體實作。

希望能夠幫助讀者在優化自身業務時,有更多的方法。

▐  開工

★  優化開始的第一件事是什麼?

對,就是找把尺子。

先确定好衡量的環境 和 資料口徑,做到手中有尺,心中不慌。

隻有明确了标準,才能使工作的價值 更清晰、更準确、更容易 得被衡量。

特别當興沖沖得找老闆彙報已經完成目标,結果一測發現還差一大截時,更能體會這條 tips 的重要。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

★  第二件 備好工具

直接代碼打性能日志

(最簡單也是最好用)

有個小建議,同時輸出時間戳 和 時間差。同時按一條主要的關鍵路徑,把先後的日志按順序串起來。SystemClock.uptimeMillis() 不推薦使用,請使用 System.currentTimeMillis() 。

這樣有個好處,當有多個系統互動,比如 前端 用戶端java 用戶端c++ 就在一條時間基線上,串起來看清晰很多。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

Systrace + TraceView + top + Charles

Systrace: 目标是跑滿 cpu,這個在項目優化初期做一下,對整個流程有個宏觀感覺。項目沖刺階段做一次,重點突破。

TraceView: 可多看看,找耗時方法很有效。

top: 在遇到分析瓶頸的時候,不妨試試,往往能看出一些線索,比如 USER CPU 占用偏高,重點分析是否和多線程場景 和 檢查 c++ 任務排程。

Charles:在定位到底是 網絡慢 還是 處理慢 的問題上,往往一針見血。

所有工具都是手段,最重要的還是對 流程的了解 和 業務的熟悉。

adb shell input tap

input tap 這個簡單的特性,往往能化腐朽為神奇,減輕偶現問題的修複驗證工作。

▐  技術選型

回憶下前面提到的店鋪渲染三大階段:整個優化工作都是圍繞這三個階段展開。這裡進一步對三步進行細化,見下圖。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

★  架構 - 子頁 并行渲染

在上述店鋪三階段中,可以發現 店鋪架構 和 店鋪首頁,作為兩個獨立 WXSDKInstance,在整體流程中耗時占比較大。很樸素地就考慮到能不能将這兩步并行起來。

優化前由前端代碼通過 embed 标簽,直接就能确定了「子頁」和「架構」嵌套關系 和 嵌套位置。流程相對簡單。

在引入并行渲染的優化後,事情變複雜。由用戶端并行承載兩個頁面的繪制工作,再選擇合适時機,把兩個頁面組合起來。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

從流程上來,「架構」和「子頁」的 weex 渲染任務會并行進行。并且同時取消了「子頁」的 loading,在使用者互動感覺上少了一個等待的過程。體感上提示很明顯。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

★  資料請求托管

經過分析,傳統方式下,一次店鋪打開,共有五次資料請求。雖然前端可以利用 Promise 技術做并發,但是依然存在等待資料的狀态。

針對上述五次請求,具體場景不同有不同的優化政策,其基本方向還是資料請求合并,并發請求,異步請求。

過程中的注意點:

1. 最優先考慮的還是資料合并,将多個資料請求合并成一個,最簡單且效果最好。

2. 并行資料請求,複用的是 weex 執行個體建立的時間,越早請求,效果越好。

3. 異步請求的場景,遠端資料擷取 和 前端資料拉取 的先後關系不定,要注意資料緩存的設計。

4. 對于所有處理資料的weex 子產品,所有方法都不需要主線程執行。

★  JS-Bundle 提前準備

總所周知,優化 JS-Bundle 的加載速度,是提升 weex 業務體驗最有效和最直接的抓手。

針對絕大多數 weex 場景,通過手淘現有Cache 技術完成 JS-Bundle 的用戶端側存儲,已經可以很好地支援業務。但對于店鋪這個 pv 億計體量的業務還略顯不足。主要展現在:

  • 到達率無法準确把控
  • 更新時間較久
  • 磁盤加載稍慢。

針對上述幾點改進空間,并且結合業務特點定制了 JS-Bundle 端側存儲能力。主要特點:

1. 除首次純新安裝場景,其餘情況總是優先傳回本地緩存的 JS-Bundle。同時根據兩次通路的時間差,判斷是否要重新整理本地緩存。且遵循 http 的 cache-control 政策。

2. 用戶端側底層存儲使用統一存儲,在手淘打開後的首次通路時,讀資料很慢,甚至在高端機上比走網絡還慢。是以選取合适的時機做預熱。

3. 記憶體級緩存。内部使用 SoftReference 包裹資料體,防止 OOM。同時在 切背景 和 接到低記憶體警告 的時候,觸發對象釋放機制。

在引入 JS-Bundle 提前準備後,使用者最直覺的感受就是整頁白屏 loading 等待的時間大幅縮短,甚至頁面轉場完成時,首頁就已經渲染完畢,避免了使用者視覺上的空窗期。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

★  上下遊優化

再提幾個比較容易忽略的優化點。

Weex Module 的調用

每次 module 調用雖然耗時很短,但是因為每次都要等待 WXJSBridge 線程空閑,并且擷取到 cpu 時間片才能執行。是以常常會出現方法調用耗時 10ms,等待耗時 300ms 的現象。這點主要從前端優化。

一方面盡量減少 module 調用,特别是關系到主鍊路的渲染流程中的 module 調用。

另一方面,将不重要的 module 調用延後。例如店鋪就将部分 ut 埋點邏輯,做了延後處理。

流程上看,通過減少 JS-bridge 的任務調用,減少線程切換時間,進而減少 weex 渲染 view 的上屏時間。最終減少使用者可流暢互動時間。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

圖檔預加載

針對圖文較多的業務,在用戶端擷取到資料接口後,直接利用圖檔庫的預請求能力,做圖檔的加載。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

硬體加速

部分手機廠商向公司開發了 API,允許在特定場景下調升 CPU 頻率,進而實作加速的效果。

最後也是最簡單的一點

減少 JS-Bundle 的文本大小,是最直覺的提效做法。

優化業務的前端代碼,去掉過期的邏輯,去除無用判斷,去掉備援的依賴。

店鋪業務大約減少了 80K+ 的 js,獲得了大約 40ms- 的性能提升。

綜上:所有優化點上線後。整個流程鍊路以下圖流程經行。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

通過店鋪性能調試工具可以實時驗證觀察各階段的渲染效果。

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

Web 前端性能優化實踐

前端 Web 頁面的性能給大多數人的印象可能還停留在幾年前的加載白屏、滑動體驗差等,但是到了現在這個時間節點,必須停下來重新思考下現有的業務發展、技術開發方式等,最終我們覺得有理由并且有能力從全鍊路的角度來看前端 Web 頁面性能,最終突破這一刻闆印象。

▐  優化思路

在進行優化前,梳理了幾個原則:

1.明确目标:整體頁面端内首屏時間 1.2s,端外首屏時間 3s,低端機首屏時間小于 2s,可互動時間小于 2.5s,可流暢互動時間小于 3s。性能優化無止盡,定好目标才能有的放矢。

2.梳理端到端系統化每個環節:梳理宏觀的每個環節,再具體分析到細節點。

3.資料驅動性能優化:線上收集各個環節的性能資料,便于分析。

4.投入産出比 ROI: 不要迷失在性能優化中,思考技術對于業務的價值。

★  各環節耗時分布

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

這張表統計了從點選開始到渲染完成各個環節的耗時分布,各個最低數值代表理論可行值,最大值代表線上真實資料耗時,是以可以清楚的知道哪個環節現階段是最差的,然後進行針對性的分析和優化。

最終會不斷的調整這張表的數值分布,然後看哪個階段還能繼續優化,形成優化并數值調整 -> 線上驗證 -> 再優化調整的正向循環。

▐  渲染方案

目前基于線上資料驗證以及線下對比,如果一個頁面資源(包括文檔 HTML、js 資源)都是走緩存的形式,整體頁面的性能、體驗會比較好,對文檔、資源的緩存非常關鍵。是以整體的渲染方案會最大限度的利用緩存,設計如下:

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

1.袋鼠服務:面向前端的資料接口合并服務,子產品直接取資料渲染,保證了頁面渲染的一緻性

2.統一渲染頁

  • 資料驅動頁面渲染,所有頁面的核心邏輯一緻
  • 保證子產品渲染與頁面的分離,確定統一渲染頁被穩定緩存

3.頁面渲染政策:差別首屏/非首屏渲染,保證首屏能快速渲染

4.子產品緩存池:解決所有頁面子產品緩存問題

▐  優化系列

基于性能優化的手段,目前我們可以主要分為以下三種:

★  加載

對于一張頁面的加載,我們要做到 4 個 1:

  • 一個共享文檔
  • 一個首屏關鍵 js
  • 一個首屏業務 combo 資源
  • 一個全局共享緩存池

主要羅列了以下幾個優化手段:

1.按需加載:某些特别大的 js 資源可以按需加載,比如安全腳本大小會有 500K+

2.分環境加載:由于目前端上 es6 環境支援的程度已經較好,是以可以在絕大多數場景去除 babel-polyfill 的依賴

3.資源緩存:端上 combo 資源按版本拆分緩存

資源緩存

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

1.端上共享緩存池,緩存 combo 後的資源到記憶體以及緩存解 combo 後的緩存到記憶體/磁盤,這樣在二次通路時達到重複利用的目的。

2.優化資源傳遞消耗,核心之間隻需 Stream 流形式傳遞,避免了類型如 byte -> String -> byteStream 之間的類型轉化消耗。

優勢:

1.無需推送 ZCache,共享單個級别的資源

2.性能好:緩存 20-100ms vs網絡請求 200-300ms+

3.優于 httpcache,可以命中更多的 combo 組合

劣勢:

1.會占用一定的記憶體來換取資源讀取上的優勢

★  渲染

對于渲染,我們要做到首屏盡可能快的渲染完成,主要羅列了以下幾個優化手段:

1.首屏/非首屏渲染,更細粒度的按行懶加載:對于一整張頁面,我們希望能優先渲染首屏,可以盡快的呈現内容讓使用者看到。有時候首屏的内容也會非常長,還可以做到按行級别的控制懶加載渲染。

2.js bridge 通道優化:端内需要有較多的與 native api 互動,通信效率也至關重要。

js bridge 通道優化

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

核心:解決JS引擎與核心 Java 層同步 IPC 通信阻塞問題

優化前

使用 prompt 方法進行 jsbridge 通信

1.JS 引擎與核心 java 層同步IPC通信,阻塞 Render 線程,通信耗時:中/低端機100-200毫秒,導緻延長渲染時間

2.引發記憶體洩漏問題

3.引起渲染引擎重排版,平均耗時 100ms 左右

優化後

使用 onConsoleMessage 方法進行 jsbridge 通信

1.JS 引擎與核心 java 層異步IPC通信,不阻塞 Render 線程

2.不引起渲染引擎重排版

★  資料

資料 Prefetch & 時機提前

核心:解決資料/渲染并行執行,盡可能的減少資料請求時間

别催更啦!手淘全鍊路性能優化下篇--容器極速之路
别催更啦!手淘全鍊路性能優化下篇--容器極速之路

1.利用了容器的初始化時間,進行并行資料請求,進而節省資料的請求時間

2.進一步提前資料請求時間點到點選

最終的節省時間 = 容器路由時間(找哪個容器渲染) + 容器啟動時間 + 容器初始化時間 + 頁面架構渲染時間

中間過程優化

核心:解決子線程與 UI 線程的阻塞問題,以及中間環節沒必要的類型轉換

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

别催更啦!手淘全鍊路性能優化下篇--容器極速之路

最後

從宏觀到微觀。性能優化之初一定要有宏觀視角,對整個架構的運作情況,要有做到心裡有數,對問題進行全面分析,然後再對瓶頸進行拆解,拆解後的子任務也不能孤立去看,一定要放在系統内,綜合選擇最優方法。

建立長期管控機制。性能優化成本也非常昂貴,性能優化在某種程度上,與其說是技術人員秀肌肉,不如說是還債,技術債(當然,真正有技術挑戰在一些特定條件下做極緻優化的情況不在此列)。從最初的技術方案設計,業務壓力下充忙上線,線性的功能堆積,對現有架構設計的妥協等多種原因導緻了性能問題的。如果在上線之初就能考慮到對性能的影響,好好設計方案,這時的成本是最低的。然而,一切依賴人的行為的機制都是不靠譜的,老虎也有打盹兒時。要減少運動式的做性能優化,需要建立一個依賴于客觀資料長效的監控機制,這也是我們正在探索的方向。

路漫漫其修遠兮,吾将上下而求索。