天天看點

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

​背景

長久以來,随着App版本的疊代,各項性能名額都會随着業務的疊代,代碼的堆疊而出現逐漸下滑的趨勢。以手機天貓Android9.1.0版本為例,整體啟動耗時一直在增長。其中50多個啟動任務在首頁加載之前就需要初始化,再加上首頁複雜的業務邏輯,進一步地增加了app啟動的耗時,這樣就極大地影響到了使用者的使用體驗。

考慮到使用者千人千面的互動行為,每個使用者在應用啟動初期進行的操作不同,涉及到的啟動任務也各有不同,是以一成不變的啟動任務初始化勢必會導緻啟動資源的浪費。為此,我們探索了使用者個性化行為和啟動任務調用之間的關聯,通過算法的能力實作啟動任務初始化的個性化編排和千人千面,對不同的使用者編排不同的啟動任務加載政策,在降低應用啟動耗時的同時,保障使用者互動使用的流暢體驗。

目前的問題

首先來分析下目前手貓Android端啟動耗時嚴重,遇到的重要阻礙和問題是什麼。

目前有50多個任務在MainActivity初始化之前就必須要執行完成。其中包括了網絡sdk,圖檔sdk,很多中間件及其他一些基礎功子產品的初始化,同時還包括了很多手貓自身業務的初始化邏輯,這些任務有一些并沒有聯系但是卻因為曆史原因牽連在一起導緻出現無人敢動的情況。

總結下來主要是下面三種情況,第一,有一些任務和sdk由于曆史原因已經不在使用,但是因為互相之間的代碼牽連,并沒有人能夠明确是否可以直接下線。第二,有一些任務并不需要在啟動時就必須執行。第三,還有一些任務也是在某些業務場景下才會觸發。并不需要直接放到啟動時進行。

現有方案

在分析了上面的問題之後,也參考了目前市面上大部分的技術方案,大家都有很多的精髓,從中吸取到很多精華。基于已有的方案我發現,現有的方案基本上都将啟動任務進行梳理,剔除不再使用的啟動任務。将其他任務按照優先級進行了分塊分層,在無法完全确定優先級順序的任務中,需要與業務同學進行溝通對焦,明确優先級。同時将比較大的任務拆分成多個小任務。将這些任務進行編排執行,一般是分成三個階段。主線程串行階段,異步首頁前執行階段,閑時階段。

目前的這些方案多會遇到一些問題。比如:

  1. 人為決策而非資料決策

大量的啟動任務,有一些業務方提出他們的業務很重要,需要提前到啟動首頁初始化之前,但是因為我們無法判斷這個任務是不是真的這麼重要,隻能進行人為的拍闆。

  1. 容易形成盲點無人了解,隔段時間就得進行重構

有一些任務可能在某些時間階段需要提前,其他時間一般不會用到或者可能就是已經下線的業務,但是如果是寫死加上人員變動,也沒人知道某個任務到底是否還在用也不敢進行下線,後續的維護成本就會很高。

  1. 重構整理時耗費時間需要找各個團隊對焦

需要進行業務啟動任務調整的時候要找所有初始化任務的業務方,每個每個對焦,確定這個任務延後執行也不會對業務有影響。

  1. 無法針對不同使用者的行為做到區分

目前的任務編排大都是在本地寫死的,順序固定,大家都“一視同仁”。而每個使用者在使用過程中的行為都是千人千面的,并沒有将這些資料很好的利用起來。

新的解決方案

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望
  1. 啟動任務動态化

目前手貓整體的50多個啟動任務,都是在本地進行寫死寫死的,每個啟動任務都可能有自己的線程池,io操作,在裝置硬體條件有限的情況下,對于CPU占用,記憶體占用都會非常緊張,且每個sdk對于初始化的時機、需要初始化的程序、初始化的線程等都各有要求。現有方案是将任務分成了三個階段,不同階段的任務在不同階段執行。如果需要動态進行編排,基于現有的方案無法通過算法進行動态改變。是以首先我們将50多個啟動任務進行抽離,放在統一的管理器裡進行管理。同時将原有的寫死的任務執行順序修改成通過動态下發的編排腳本的方式進行任務的編排和啟動。

  1. 設計啟動任務編排方式,提供下發變更邏輯

由于任務本身之間的關聯關系,C任務可能需要在A、B兩個任務執行之後才能執行。整體的任務編排符合有向無環圖,是以我們在讀取的時候采用反向設計,将每個任務按照連結清單的思想進行設計,在每個任務之下,将目前任務的父任務都挂在這個任務後面,以此類推。友善後面讀取時直接關聯父任務。

  1. 通過AOP插裝技術埋點

通過AOP插裝技術,統計出每個啟動任務在首頁啟動之後并多長時間才開始第一次使用到。為什麼要是用aop,因為目前使用到的一些啟動任務,其中分成了兩類,一部分在自己的代碼中有過一次封裝可以直接進行埋點。但是另外一部分是三方庫,不同的業務方使用的地方也很分散比較難統計,這時候就需要aop技術來進行統一的埋點了。

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

我們采用的技術方案是Gradle plugin+Transform+ASM,根據上圖可以明白,因為有一些三方庫我是沒有源碼的。隻能在打包的過程中在由class打包進dex的過程中修改位元組碼進行代碼插樁。大家知道通過gradle插件通過task執行的時候,上一個task的輸出必須正确灌入到下個task的輸入中才行。是以我擴充了gradle-plugin中增加了自己的一部分代碼插樁邏輯,完成了多個中間件sdk埋點的工作。具體步驟如下圖:

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

另一方面,雖然經過算法編排之後根據相應的算法模型名額,能夠解決大部分使用者的使用時出現的降級問題,但是仍然會有一些漏網之魚,為了防止使用者在使用時出現降級情況,我們在使用者第一次使用某個啟動任務之時,如果發現這個任務并未初始化完成,則重新進行一次初始化,減少使用者在使用時出現降級的問題。

4.穩定性驗證

完成代碼插樁工作之後,進行了大量的啟動性能測試和整體的穩定性測試,確定啟動任務的修改不會對app的使用産生影響,使用的手機是vivo Y67,通過低端機進行monkey等之後開始灰階驗證,確定整個性能測試過程中無影響的crash,anr和啟動白屏或無法啟動等情況出現。

  1. 啟動任務編排政策的千人千面

前文提到了啟動任務原本都是在app啟動階段進行初始化加載的,對于那些完成初始化加載,但是并沒有很快被使用者調用的情況,這樣會浪費資源,增加啟動耗時;那麼如果将所有裝置的啟動任務全部推遲在使用者互動階段的閑時進行加載呢?這樣必然可以降低裝置的啟動耗時,但是,也會在使用者體驗方面造成一些負面影響。為了清晰的描述啟動任務加載和調用的過程,分析什麼樣的情況适合将啟動任務推遲在閑時加載,什麼樣的情況應當保持啟動任務在啟動階段加載,我們将啟動任務的加載調用過程分成了以下3種情況:

啟動階段調用:app啟動階段調用任務,此時app啟動尚未完成,如果推遲至閑時加載,調用任務時需等待任務加載。

閑時階段調用1:app啟動完成後,使用者互動階段調用任務,但是調用時間較早,如果推遲至閑時加載,調用任務時,任務沒有足夠時間加載完成,仍需等待任務加載。

閑時階段調用2:app啟動完成後,使用者互動階段調用任務,但是調用時間較晚甚至不調用任務,如果推遲至閑時加載,不會造成不利影響。

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

通過上述分析,我們可以确定對于同一個啟動任務,全部推遲至閑時加載并不是最好的選擇。對應啟動階段調用和閑時階段調用1這兩種情況,如果推遲任務至閑時加載,會在調用任務時出現等待任務加載的情況,對使用者造成不好的體驗。是以,我們希望利用算法的能力,盡可能的找出符合閑時階段調用2情況的裝置,隻将這部分裝置的任務推遲,實作啟動任務加載的千人千面,不僅有效地降低啟動耗時,同時提升使用者體驗。為此,我們以A啟動任務作為試點,開始個性化編排的探索。

為什麼要進行個性化的探索,我們認為可以從使用者和裝置兩個次元來思考個性化這件事:

使用者次元:每個使用者有不同的習慣,有的人打開app會立馬搜尋商品,有的人會先浏覽猜你喜歡等頁面,那麼可能不同的使用習慣,可能導緻任務在不同的時候被調用;

裝置次元:不同品牌的裝置性能不同,加載用戶端的機制和效率存在差異,對于高端機,不需要推遲任務就可以實作快速啟動,而低端機需要更快的啟動時間來保證使用者體驗;

綜上所述,我們從兩個次元建構了特征資料,涵蓋了使用者的基礎資訊、行為資料、業務資料、裝置性能、A任務的埋點資料等。

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

對于同一個使用者和裝置,其在不同時刻使用app的狀态不同,啟動任務的調用情況也不盡相同,是以理論上應當實作啟動任務的實時編排,但是考慮到頻繁的變更啟動政策可能對用戶端穩定性造成影響,我們更多地選擇使用者和裝置的長期資料進行預測。

前文中我們提到,找出閑時階段2的裝置,并将其任務推遲是我們的目标。然而從實際資料中可以看出,每個裝置的啟動資料并非一成不變,在不同狀态下可能出現啟動階段調用、閑時階段調用1和閑時階段調用2這三種情況,而且由于使用者的活躍度不同,有的使用者登入頻繁,而有的使用者幾天隻來一次,可利用的資料量不同。是以,相比直接将問題定義為二分類問題,我們綜合裝置在采集周期内的調用A任務次數all,和調用A任務在不同階段次數s1,s2,s3對裝置打分标注,圈出高分裝置推遲A任務,低分裝置維持A任務在啟動階段不動:

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

由于啟動任務千人千面的一期工作更多是有效性的探索驗證,因為我們采用了集團内高效的PS-SMART算法完成模型訓練,并基于模型預測結果對裝置進行圈選,區分适合啟動階段加載和閑時階段加載的裝置,最終将配置同步到用戶端,在裝置初次啟動時拉取配置,改變後續啟動政策,整體過程如下圖:

再快0.8秒!天貓工程師如何用千人千面優化啟動速度?​背景現有方案新的解決方案遇到的問題和解法資料成果總結和展望

針對線上實驗桶,我們利用算法圈出部分的裝置在啟動階段加載的個任務,其餘的裝置推遲至閑時階段加載。綜合線上資料,裝置的啟動耗時優化下降了170ms。

遇到的問題和解法

因為本次針對啟動優化的改動設計的影響面比較大,一旦出現問題可能就會導緻app無法打開或者啟動crash的問題,造成的問題也是比較嚴重。是以我們針對異常的情況采用了一些降級方案。

首先,當監測到使用者有三次啟動crash時則直接降級到舊版本的代碼方案。

第二,因為更新過來的新腳本是儲存在sp中的,這樣就難免會出現丢失的情況,是以增加了兜底政策,在assets和記憶體中都儲存了一份原始腳本檔案。確定最終都有一份啟動任務腳本可以執行。

第三,在任務編排之後,難免會出現某些使用者成為算法的漏網之魚,導緻編排到啟動之後的任務在很早就開始掉用了。為了解決這個問題,我們在啟動任務第一次使用時進行判斷,如果該任務已經還未進行初始化則直接手動拉起一次。確定不影響業務的正常運轉。

第四,為了減少和防止出現較大的問題,增加了一鍵切換的總開關。

資料成果

經過整體的對啟動任務的智能編排。目前有5個任務進行了智能化的延後。目前整體啟動時間下降了0.8秒,90%啟動分位數下降了1.5秒,低端機下降了1.6s。

總結和展望

在文章完成時,我們已經具備了通過啟動任務第一次使用的資料埋點來分析并進行任務順序調整的能力,改變了以前排查下線啟動任務困難,隻能通過業務對焦和手動測試變更啟動任務的問題。

同時也第一次在應用啟動時使用到了算法的能力,使用了幾個啟動任務作為試點,讓大資料和算法能力不僅僅隻能局限在内容和商品推薦上,擴充了算法能力再端上應用的邊界。後續我們也會在更多的啟動任務中進行嘗試。

再次,使用者體驗,特别是啟動優化也是一個需要長久投入和分析研究的事情,我們也會進一步思考更好的方式來得到更好的使用者體驗。

【關于我們】

手貓技術團隊目前緻力于拓展新零售業務下的新場景,發掘業務的新形态。同時也在持續地探索與AI人工智能結合,一方面擴充算法的使用邊界,另一方面通過大資料讓App變得更智能。在這裡你可以接觸到很多無線開發之外的創新技術,擴充技術的寬度。歡迎加入我們:[email protected]

繼續閱讀