優酷 Android 構建速度優化實踐

阿里巴巴移動技術發表於2021-12-27

作者:蘇彥郊(木磊)

Android 專案一般使用 gradle 作為構建打包工具,gradle 簡潔、動態的功能特性為人津津樂道,同樣,構建執行速度緩慢的缺陷也一直為人詬病。

近年來,隨著優酷功能特性日益豐富,優酷的程式碼規模也急劇增加,同時,龐大的程式碼規模也帶來了構建耗時的不斷增加。整包構建耗時一度高達35min,嚴重影響整合與迭代效率。因此構建速度優化勢在必行。截止 2021年 11 月份,優酷構建耗時優化取得較為理想的優化結果(如下),現將構建速度優化的實踐方案記錄成文。

android構建型別2020年2021
android debug 包構建耗時12min2.5min
android release 包構建耗時35 min12min

方案與收益統計圖:

優化思路

技術優化類專案一般採用照設定資料指標、技術優化、成果防腐化三個維度展開。套用技術優化類項拆解可知,我們需要完成如下三個子專案:

  1. 設定資料指標:即收集與選取核心優化的資料指標,體現成果價值。本文選取構建耗時、構建失敗率、小時維度構建次數等指標作為成果優化的資料支撐;
  2. 技術優化:通過影響構建速度的影響因素可知,包括軟體與硬體兩部分,所以構建速度優化可分為軟體優化與硬體優化兩大方向;
  3. 成果防腐化: 即維持技術優化指標不惡化,保障優化成果。

接下來,我將按照設定資料指標與結果防腐、技術優化——軟體優化、技術優化——硬體優化三個部分展開。

優化方案

設定資料指標與結果防腐

優化類專案需要建立健全相應的資料指標體系,藉由資料評判體積優化項與優化方案進行有效性判定。進行構建優化前,筆者基於阿里巴巴Aone FaaS( Severless 服務)平臺搭建了資料評判與監控大盤。該大盤有構建型別、構建時間、構建成功率、構建任務耗時等多項指標,滿足構建優化專案需要根據型別、任務頻率、高耗時任務排查的需求。

完成相關資料能力建設後,通過構建關鍵資料指標——構建耗時與構建成功率的追蹤和分析,進而得出構建耗時的主要影響因素為高耗時任務,構建成功率的主要影響因素為不合理的構建任務。因此,我們可以通過高耗時任務報警與不合理任務報警,快速發現並分析構建速度惡化情形,進而保障構建耗時優化成果。

軟體優化

構建側去atlas

Atlas是伴隨著手機淘寶的不斷髮展,進而衍生出來的一個執行於android系統上的容器化框架,我們也叫動態元件化(Dynamic Bundle)框架。它主要提供瞭解耦化、元件化、動態性的支援。覆蓋了工程師的工程編碼期、Apk執行期以及後續運維期的各種問題。

依託於深度定製的產物結構與高度複雜、深度hook 的執行時框架,Atlas可以視為移動端 OSGI 實現方案與元件化方案。但隨著優酷移動端架構調整與自研遠端化方案落地,Atlas執行時框架逐漸喪失了OSGI框架作用,遂在執行期去除Atals 框架。

當執行期將Atlas依賴去除後,Atlas的複雜構建流程(如下圖所示)也失去了存在意義。隨即,優酷啟動了構建側Atlas 去除專案,目標是將Atlas構建外掛去除、構建原生化、純淨化、精簡化。通過產物原生化、構建任務清理、工具鏈升級等一系列動作,在完成了構建側Atlas去除目標的同時,構建效能也有部分提升。

收益:debug 包構建耗時降低 3min左右。release 包構建耗時降低4min-5min。

gradle 升級和android gradle plugin 升級

gradle 團隊一直在持續優化gradle 的構建速度等效能指標, 同時 google 團隊也在持續優化 android gradle plugin 構建工具效能。為了進一步提升優酷android 端構建效能,決定對優酷android構建系統進行升級,將android gradle plugin 構建工具版本由3.0.1(2017年)提升至3.4.3(2019年)版本,將gradle構建工具由4.4(2017)至5.5 (2019年)版本。

對比升級前後構建耗時,可以發現構建工具升級後,效能提升主要源於三個方面:

  1. 隨android gradle plugin 升級,aapt2、proguard 等構建工具也進行了升級,這部分工具升級後,構建效能有小幅提升;
  2. 更好任務排布與並行化機制:升級gradle 與agp 後,agp 3.4.3 版本進行了簽名、壓縮、對齊任務的整合優化;
  3. 配置按需載入與非同步化策略:android gradle plugin 3.4.3 採用資源的非同步載入策略,即configuaration 階段僅做依賴拉取工作,不再進行產物的解壓、過濾、合併工作,這樣可以有效避免io 擁塞問題,避免cpu 忙等現象。

收益:debug 包構建耗時降低 2min左右 。release 包構建耗時降低4min左右。

dx 構建優化

升級到 android gradle plugin 3.4.2 版本後,agp 新增了三個個dx構建引數,經過測試可以顯著提升dx處理class檔案速度。經過測試 設定如下三個屬性降低構建耗時。

android.dexingNumberOfBuckets=16 
android.dexingWriteBuffer.size=256 
android.dexingReadBuffer.size=256

仔細閱讀agp原始碼可知,這幾個引數構建構建記憶體中dx 快取大小、dex 讀取寫入片大小。預設dexingNumberOfBuckets 為cpu個數的一半、讀取寫入大小為1KB。這樣就造成高io情形下,cpu 忙等情形。採用降低磁碟寫入次數、增加快取方式能夠明顯降低構建耗時。

收益:debug release 包構建耗時降低3min左右。

冗餘任務整理

隨著我們不斷迭代、平臺升級,一些 構建任務、構建功能等已經廢棄,但由於構建系統的特殊性——無產物回滾能力,導致構建系統的任務一直處於單增狀態。且由於構建系統高風險、低收益、邏輯功能複雜陳舊的特點,導致構建速度治理動力不足。

針對上述問題,通過構建邏輯梳理、構建配置項清理、 單任務除錯等手段,逐步摸清構建每個構建任務的功能,並對postPackageDebug等30+ 無用任務進行清理、對transform管理、任務管理等核心功能進行簡化。下表為構建冗餘任務清理列表。

任務名作用產出是否保留
postPackageDebugapk後處理可以廢棄
remoteSignAppDebug遠端簽名可以廢棄
DexCountDebugdex個數可以廢棄
ChannelPackageDebug渠道包構建可以廢棄
generateAppInfoDebugappinfo生成可以廢棄
uploadBuildFilesDebug檔案上傳可以廢棄
buildPatchBaseApkDebug熱修復構建可以廢棄
....

收益:debug 包構建耗時降低 15s+ ,release 包構建耗時降低20s+。

任務pipeline化

gradle 採用任務情形進行構建任務排布,基於這一擴充特性可以對產物進行後置處理。例如對 apk 後置處理有渠道處理、arsc 處理、對齊、簽名、分包處理、圖片壓縮等任務,每一個任務都需要apk進行反覆的解壓、壓縮、拷貝操作,浪費cpu 、系統io,徒增構建耗時。

為了降低apk構建耗時、簡化apk產物操作複雜,我們對現有任務進行重整與擴充、實現了一種低拷貝、一次解壓、一次壓縮的產物pipeline處理機制,下圖為構建過程apk後處理機制流程圖。

收益:debug 包耗時降低21s,release 包耗時降低11s。

構建模版優化

按照用途區分 android 有多種構建變種,debug版本、release版本、遠端與非遠端等。對於開發階段來說,一些外掛的優化功能如turbo dex 縮減、7zip 壓縮完全無必要,可以直接禁止掉。

收益:debug 縮短1-2min 左右,release 包無變化。

縮減程式碼規模

構建耗時中 java 程式碼混淆耗時約佔整體構建耗時的60%,同時混淆耗時與程式碼規模正相關。所以構建耗時與程式碼規模成正相關關係。

程式碼規模的膨脹一部分來源於業務擴張,一部分來源於冗餘程式碼的工程腐化。自2020年下半年至 2021年上半年期間,優酷進行了常態化的包體積治理,包體積取得了較為優異的成果,程式碼腐化問題也得到了部分緩解。

如下圖所示優酷android 端自2020下半年至今,可知 android 端 java 程式碼規模降低25%,構建速度貢獻約為45s+。

收益:release 包構建耗時降低約為45s+。debug 包構建耗時約為5s-10s。

硬體優化

私有構建租戶池

採用iostat、tsar 等linux 效能分析工具對優酷android 端構建過程分析(如下圖)可知。整個構建流程cpu io-wait 現象嚴重,即構建過程中存在大量的io操作,由於構建機器io效能不足,導致構建耗時偏長, 側面印證軟體優化中降低io能夠構建速度的有效性。

針對io瓶頸問題,主要有兩種解決辦法:採用buffered io 處理、提升 Io 效能:

  1. 首先,僅保留agp外掛進行構建,發現並無構建速度明顯提升。證明:自定義外掛無io 優化空間;
  2. 其次,對android 構建流程分析,io 碎片化寫入較少,所有采用buffered io 處理 io 瓶頸,優化空間不大;
  3. 最後,通過採用使用SSD 替換機械硬碟效果較好。物理機構建比較資料如下:
機器型別非首次構建 (依賴快取情形) 主要情形首次構建(無依賴快取情形)硬體情況
組一(桌上型電腦)12min57s21 minSSD Ex900 521G / 寫入峰值約900Mb/s / intel 2.9Ghz 16執行緒 /16G記憶體
組二(Dell R740 刀鋒伺服器)25min40min機械硬碟 /寫入峰值約254Mb/s / 驍龍 2.1Ghz 24執行緒/ 48G記憶體
組三(Dell WorkStation)19min58s27minSSD EX900 521G / 寫入峰值約900Mb/s /驍龍 2.2Ghz 20執行緒 /32G記憶體
組四(桌上型電腦)10min10s23minSSD 512G / 寫入峰值約1G/s AMD 3.5Ghz 24執行緒 32G記憶體
組五(devops叢集)23min40min多為機械硬碟,視排程具體機器而定

收益:debug 減少5min 左右,release 包減少10min。

總結

綜上所述,為了維護構建速度優化的成果,我們可以進行如下方面的工作:

  1. 為了滿足資料指標設定與構建優化防腐的需求,我們需要設定構建優化指標,建立合理的資料評測體系;
  2. 通過對構建模板拆分、冗餘任務清理、gradle 升級和android gradle plugin 升級、合理設定構建相關引數——dx構建優化等軟體優化手段,我們可以獲得大部分構建速度優化成果;
  3. 硬體優化部分需要建立構建流程關鍵瓶頸的分析上,各應用構建瓶頸可能不同。

受限於技術手段與穩定性問題, 構建速度優化還有如下未完成部分。

  1. 混淆規則控制與清理:混淆任務執行速度與混淆規則數量存在正相關關係,不合理的混淆規則會導致混淆任務耗時增長;
  2. 工程腐化程度治理:無用程式碼規模是工程腐化程度的重要標誌,同時無用程式碼規模是影響構建速度的重要因素。但是如何治理大型專案的工程腐化程度,是應用架構以及全體應用開發需要探索的下一個重要課題;
  3. r8構建優化:通過對android dex 構建工具鏈——r8 的升級測試發現,優酷release 包構建有較為明顯的構建速度提升。但由於部分google bug 導致r8 優化delay。

未來,優酷技術團隊也將針對上述問題,持續優化構建耗時,歡迎大家隨時與我們交流討論。

【相關文件】

我們招聘啦!

優酷 — 技術中心 — 架構團隊招人,描述如下。

【職位描述】

  1. 負責以優酷為核心的android基礎架構工作,包括基礎框架、中介軟體等。
  2. 負責app的穩定性、效能、包瘦身等長效治理工作,提升基礎使用者體驗。
  3. 研究移動端前沿技術工程化,以及技術趨勢探索。
  4. 解決各類疑難問題,支撐業務快速、穩定、高效迭代。

【職位要求】

  1. 熟悉Android SDK、Framework等基礎技術,並有較好的原始碼分析能力。
  2. 精通Java語言,基本功紮實,有Kotlin、C/C++開發能力者優先。
  3. 對Dalvik/Art虛擬機器中,任意部分有經驗者優先。
  4. 有關鍵技術選型、疑難Bug修復、記憶體優化等經驗者優先。

簡歷投至方式yanjiao.syj@alibaba-inc.com

關注【阿里巴巴移動技術】微信公眾號,每週 3 篇移動技術實踐&乾貨給你思考!

相關文章