作者:蘇彥郊(木磊)
Android 專案一般使用 gradle 作為構建打包工具,gradle 簡潔、動態的功能特性為人津津樂道,同樣,構建執行速度緩慢的缺陷也一直為人詬病。
近年來,隨著優酷功能特性日益豐富,優酷的程式碼規模也急劇增加,同時,龐大的程式碼規模也帶來了構建耗時的不斷增加。整包構建耗時一度高達35min,嚴重影響整合與迭代效率。因此構建速度優化勢在必行。截止 2021年 11 月份,優酷構建耗時優化取得較為理想的優化結果(如下),現將構建速度優化的實踐方案記錄成文。
android構建型別 | 2020年 | 2021 |
---|---|---|
android debug 包構建耗時 | 12min | 2.5min |
android release 包構建耗時 | 35 min | 12min |
方案與收益統計圖:
優化思路
技術優化類專案一般採用照設定資料指標、技術優化、成果防腐化三個維度展開。套用技術優化類項拆解可知,我們需要完成如下三個子專案:
- 設定資料指標:即收集與選取核心優化的資料指標,體現成果價值。本文選取構建耗時、構建失敗率、小時維度構建次數等指標作為成果優化的資料支撐;
- 技術優化:通過影響構建速度的影響因素可知,包括軟體與硬體兩部分,所以構建速度優化可分為軟體優化與硬體優化兩大方向;
- 成果防腐化: 即維持技術優化指標不惡化,保障優化成果。
接下來,我將按照設定資料指標與結果防腐、技術優化——軟體優化、技術優化——硬體優化三個部分展開。
優化方案
設定資料指標與結果防腐
優化類專案需要建立健全相應的資料指標體系,藉由資料評判體積優化項與優化方案進行有效性判定。進行構建優化前,筆者基於阿里巴巴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年)版本。
對比升級前後構建耗時,可以發現構建工具升級後,效能提升主要源於三個方面:
- 隨android gradle plugin 升級,aapt2、proguard 等構建工具也進行了升級,這部分工具升級後,構建效能有小幅提升;
- 更好任務排布與並行化機制:升級gradle 與agp 後,agp 3.4.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管理、任務管理等核心功能進行簡化。下表為構建冗餘任務清理列表。
任務名 | 作用產出 | 是否保留 |
---|---|---|
postPackageDebug | apk後處理 | 可以廢棄 |
remoteSignAppDebug | 遠端簽名 | 可以廢棄 |
DexCountDebug | dex個數 | 可以廢棄 |
ChannelPackageDebug | 渠道包構建 | 可以廢棄 |
generateAppInfoDebug | appinfo生成 | 可以廢棄 |
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 效能:
- 首先,僅保留agp外掛進行構建,發現並無構建速度明顯提升。證明:自定義外掛無io 優化空間;
- 其次,對android 構建流程分析,io 碎片化寫入較少,所有采用buffered io 處理 io 瓶頸,優化空間不大;
- 最後,通過採用使用SSD 替換機械硬碟效果較好。物理機構建比較資料如下:
機器型別 | 非首次構建 (依賴快取情形) 主要情形 | 首次構建(無依賴快取情形) | 硬體情況 |
---|---|---|---|
組一(桌上型電腦) | 12min57s | 21 min | SSD Ex900 521G / 寫入峰值約900Mb/s / intel 2.9Ghz 16執行緒 /16G記憶體 |
組二(Dell R740 刀鋒伺服器) | 25min | 40min | 機械硬碟 /寫入峰值約254Mb/s / 驍龍 2.1Ghz 24執行緒/ 48G記憶體 |
組三(Dell WorkStation) | 19min58s | 27min | SSD EX900 521G / 寫入峰值約900Mb/s /驍龍 2.2Ghz 20執行緒 /32G記憶體 |
組四(桌上型電腦) | 10min10s | 23min | SSD 512G / 寫入峰值約1G/s AMD 3.5Ghz 24執行緒 32G記憶體 |
組五(devops叢集) | 23min | 40min | 多為機械硬碟,視排程具體機器而定 |
收益:debug 減少5min 左右,release 包減少10min。
總結
綜上所述,為了維護構建速度優化的成果,我們可以進行如下方面的工作:
- 為了滿足資料指標設定與構建優化防腐的需求,我們需要設定構建優化指標,建立合理的資料評測體系;
- 通過對構建模板拆分、冗餘任務清理、gradle 升級和android gradle plugin 升級、合理設定構建相關引數——dx構建優化等軟體優化手段,我們可以獲得大部分構建速度優化成果;
- 硬體優化部分需要建立構建流程關鍵瓶頸的分析上,各應用構建瓶頸可能不同。
受限於技術手段與穩定性問題, 構建速度優化還有如下未完成部分。
- 混淆規則控制與清理:混淆任務執行速度與混淆規則數量存在正相關關係,不合理的混淆規則會導致混淆任務耗時增長;
- 工程腐化程度治理:無用程式碼規模是工程腐化程度的重要標誌,同時無用程式碼規模是影響構建速度的重要因素。但是如何治理大型專案的工程腐化程度,是應用架構以及全體應用開發需要探索的下一個重要課題;
- r8構建優化:通過對android dex 構建工具鏈——r8 的升級測試發現,優酷release 包構建有較為明顯的構建速度提升。但由於部分google bug 導致r8 優化delay。
未來,優酷技術團隊也將針對上述問題,持續優化構建耗時,歡迎大家隨時與我們交流討論。
【相關文件】
- linux 效能優化指南:https://www.processon.com/vie...
- r8 相關問題:https://issuetracker.google.c...
- Atlas:https://github.com/alibaba/atlas
我們招聘啦!
優酷 — 技術中心 — 架構團隊招人,描述如下。
【職位描述】
- 負責以優酷為核心的android基礎架構工作,包括基礎框架、中介軟體等。
- 負責app的穩定性、效能、包瘦身等長效治理工作,提升基礎使用者體驗。
- 研究移動端前沿技術工程化,以及技術趨勢探索。
- 解決各類疑難問題,支撐業務快速、穩定、高效迭代。
【職位要求】
- 熟悉Android SDK、Framework等基礎技術,並有較好的原始碼分析能力。
- 精通Java語言,基本功紮實,有Kotlin、C/C++開發能力者優先。
- 對Dalvik/Art虛擬機器中,任意部分有經驗者優先。
- 有關鍵技術選型、疑難Bug修復、記憶體優化等經驗者優先。
簡歷投至方式:yanjiao.syj@alibaba-inc.com
關注【阿里巴巴移動技術】微信公眾號,每週 3 篇移動技術實踐&乾貨給你思考!