Android效能優化方法論

語言: CN / TW / HK

作為一名開發,效能優化是永遠繞不過去的話題,在日常的開發中,我們可肯定都會接觸過。Android 的效能優化其實是非常成熟的了,成熟的套路,成熟的方法論,成熟的開源框架等等。

對於接觸效能優化經驗較少的開發者來說,可能很少有機會能去總結或者學到這些成熟的套路,方法論,或者框架。所以作為一位多年長期做效能優化的開發者,在這篇文章中對效能優化的方法論做一些總結,以供大家借鑑。

效能優化的本質

首先,我先介紹一下效能優化的本質。我對其本質的認知是這樣的:效能優化的本質是合理且充分的使用硬體資源,讓程式的表現更好,並且程式表現更好的目的則是為了獲取更多來自客戶的留存,使用時長,口碑、利潤等收益。

所以基於本質來思考,效能優化最重要的兩件事情:

  1. 合理且充分的使用硬體資源
  1. 讓程式表現更好,並取得收益

下面講一下這兩件事情。

合理且充分的使用硬體資源

充分表示能將硬體的資源充分發揮出來,但充分不一定是合理的,比如我們一下子打了幾百個執行緒,cpu 被充分發揮了,但是並不合理,所以合理表示所發揮出來的硬體資源能給程式表現有正向的作用。

硬體資源包括:CPU,記憶體,硬碟,電量,流量(不屬於硬體資源,不過也歸於需要合理使用的資源之一)等等。

下面舉幾個合理且充分的使用硬體資源的例子:

  1. CPU 資源的使用率高,但並不是過載的狀態,並且 cpu 資源主要為當前場景所使用,而不是被全業務所分散消耗。比如我們優化頁面開啟速度,速度和 cpu 有很大的關係,那麼我們首先要確保 cpu 被充分發揮出來了,我們可以使用多執行緒、頁面開啟前提前預載入等策略,來發揮手機的 cpu。但是在開啟頁面的時候,我們要合理的確保 cpu 資源主要被開啟頁面相關的邏輯所使用,比如元件建立,資料獲取,頁面渲染等等,至於其他和當前開啟頁面場景聯絡較少的邏輯,比如週期任務,監控,或者一些預載入等等都可以關閉或者延遲,以此減少非相關任務對 cpu 的消耗,
  1. 記憶體資源緩使用充分,並且又能將 OOM 等異常控制在合理範圍內。比如我們做記憶體優化,記憶體優化並不是越少越好,相反記憶體佔用多可能讓程式更快,但是記憶體佔用也不能太高,所以我們可以根據不同檔次機型的 OOM 率,將記憶體的佔用控制在充分使用並且合理的狀態,低端機上,通過功能降級等優化,減少記憶體的使用,高階機上,則可以適當提升記憶體的佔用,讓程式表現的更好。
  1. ……

讓程式表現更好,並取得收益

我們有很多直接的指標來度量我效能優化取得的收益,比如做記憶體優化可以用 pss,java 記憶體佔用,native 記憶體佔用等等;做速度優化,可以用啟動速度,頁面開啟速度;做卡頓優化,這用幀率等等。掌握這些指標很重要,我們需要知道如何能正確並且低開銷的監控這些指標資料。

除了上面的直接指標外,我們還需要了解效能優化的最終體現指標,使用者留存率,使用時長,轉換率,好評率等指標。有時候,這些指標才是最終度量我們效能優化成果的資料,比如我們做記憶體優化,pss 降低了 100 M,但僅僅只是記憶體佔用少了 100M 並沒有太大的收益,如果這個 100M 體現在對應用的存活時間,轉化率的提升上,那這 100 M 的優化就是值得的,我們再向上報告我們產出時,也更容易獲得認可。

如何做好效能優化

講完了效能優化的本質,我再講講如何做好效能優化。我主要從下面這三個方面來講解

  1. 知識儲備
  1. 思考的角度和方式
  1. 形成完整的閉環

知識儲備

想要做好效能優化,特別是原創性、或者完善並且體系的、或者效果很好的優化,不是我們從網上看一些文章然後模仿一下就能進行,需要我們有比較紮實的知識儲備,然後基於這些知識儲備,通過深入思考,去分析我們的應用,尋找優化點。我依然舉一些例子,來說明硬體層面,系統層面和軟體層面的知識對我們做好效能優化的幫助。

硬體層面

在硬體層面,我們需要處理器的體系結構,儲存器的層次結構有一定的瞭解。如果我們如果不知道 cpu 由幾個核組成,哪些是大核,哪些是小核,我們就不會想到將核心執行緒繫結大核來提升效能的優化方案;如果我們不瞭解儲存結構中暫存器,快取記憶體,主存的設計,我們就沒法針對這一特效來提升效能,比如將核心資料儘量放在快取記憶體中就能提升不少速度相關的效能。

系統層面

對作業系統的熟悉和了解,也是幫助我們做好效能優化不可缺少的知識。我在這裡列一下系統層面需要掌握的知識,但不是全的,Linux的知識包括進行管理和排程,記憶體管理,虛擬記憶體,鎖,IPC通訊等。Android系統的知識包括虛擬機器,核心服務如ams,wms等等,渲染,以及一些核心流程,如啟動,開啟activity,安裝等等。

如果我們不瞭解Linux系統的程序排程系統,我們就沒法充分利用程序優先來幫助我們提升效能;如果我們不熟悉 Android 的虛擬機器,那麼圍繞這虛擬機器一些相關的優化,比如 oom 優化,或者是 gc 優化等等都無法很好的開展。

軟體層面

軟體層面就是我們自己所開發的 App,在效能優化中,我們需要對自己所開發的應用盡可能得熟悉。比如我們需要知道自己所開發的 App 有哪些執行緒,都是幹嘛的,這些執行緒的 cpu 消耗情況,記憶體佔用多少,都是哪些業務佔用的,快取命中率多少等等。我們需要知道自己所開發的 App 有哪些業務,這些使用都是幹嘛的,使用率多少,對資源的消耗情況等等。

除了上面提到的三個層面的知識,想要深入做好效能優化,還需要掌握更多的知識,比如彙編,編譯器、程式語言、逆向等等知識。比如用c++ 寫程式碼就比用java寫程式碼執行更快,我們可以通過將一些業務替換成 c++ 來提高效能;比如編譯期間的內聯,無用程式碼消除等優化能減少包體積;逆向在效能優化上的用處也非常大,通過逆向我們可以修改系統的邏輯,讓程式表現的更好。

可以看到,想要做好效能優化,需要龐大的知識儲備,所以效能優化是很能體現開發者技術深度和廣度的,這也是面試時,一定會問效能優化相關的知識的原因。這是知識儲備不是一下就能形成的,需要我們慢慢的進行學習和積累。

思考的角度及方式

講完了知識儲備,再講講思考的角度和方式。需要注意它和知識儲備沒有先後關係,並不是說要有了足夠的技術知識後才能開始考慮如何思考。思考的角度和方式體現在我們開發的所有生命週期中,即使是新入門的開發,也可以鍛鍊自己從不同的角度和方式去進行思考。下面就聊一聊我在做效能優化的過程中,在思考的角度和方式上的一些認知。為了讓大家能更形象的理解,我就都以啟動優化來講解。

思考角度

我這裡主要通過應用層,系統詞,硬體層這三個角度來介紹我對啟動速度優化的思考。

應用層

做啟動速度優化時,如果從應用層來考慮,我會基於業務的維度考慮所載入的業務的使用率,必要性等等,然後制定優先順序,在啟動的時候只加載首屏使用,或者使用率高的業務。所以接著我就可以設計啟動框架用來管理任務,啟動框架要設計好優先順序,並且能對這些初始化的任務有使用率或者其他效能方面的統計,比如這些任務初始化後,被使用率的概率是多少,又或者初始化之後,對業務的表現提升提現在哪,幫助有多大。

從應用層的思考主要是基於對業務的管控或者對業務進行優化來提升效能。

系統層

以及系統層來考慮啟動優化也有很多點,比如執行緒和執行緒優先順序維度,在啟動過程中,如何控制好執行緒數量,如何提高主執行緒的優先順序,如何減少啟動過程中不相關的執行緒,比如 gc 執行緒等等。

硬體層

從硬體層來考慮啟動優化,我們可以從 cpu 的利用率,快取記憶體cache的命中率等維度來考慮優化。

除了上面提到的這幾個角度,我們還可以有更多角度。比如跳出本裝置之外來思考,是否可以用其他的裝置幫助我們加速啟動。google play 就有類似的優化,gp會上傳一些其他機器已經編譯好的機器碼,然後相同的裝置下載這個應用時,也會帶著這些編譯好的機器碼一起下載。還有很常用的服務端渲染技術,也是讓服務端線渲染好介面,然後直接暫時靜態模組來提升頁面開啟速度;又或者站在使用者的角度去思考,想一想到底什麼樣的優化對使用者感知上是有好處的,比如有時候我們再做啟動或者頁面開啟速度優化,會給使用者一個假的靜態頁面讓使用者感知已經打開了,然後再去繫結真實的資料。

做效能優化時,考慮的角度多一些,全面一些,能幫助我們想出更多的優化方案。

思考方式

除了鍛鍊我們站在不同的角度思考問題,我們還可以鍛鍊自己思考問題的方式,這裡介紹自上而下和自下而上兩種思考方式。

自上而下

我們做啟動優化,自上而下的優化思路可能是直接從啟動出發,然後分析啟動過程中的鏈路,然後尋找耗時函式,將耗時函式放子執行緒或者懶載入處理,但是這種方式會導致優化做的不全面。比如將耗時的任務都放在子執行緒,我們再高階機上速度確實變快了,但是在低端機上,可能會降低了啟動速度,因為低端機的 cpu 很差,執行緒一多,導致 cpu 滿載,主執行緒反而獲取不到執行時間。其次,如果從上層來看,一個函式執行耗時久可能並不是這個函式的問題,也可能是因為該函式長時間沒有獲取到 cpu 時間。

自上而下的思考很容易讓我們忽略本質,導致優化的效果不明顯或者不完整。

自下而上

自下而上思考就是從底層開始思考,還是以啟動優化為例子,自下而上的思考就不是直接分析啟動鏈路,尋找慢函式,而是直接想著如何在啟動過程中合理且充分的使用 cpu 資源,這個時候我們的方案就很多了,比如我們可能會想到不同的機型 cpu 能力是不一樣的,所以我們會針對高階機和低端機來分別優化,高階機上,我們想辦法讓cpu利用率更高,低端機上想辦法避免 cpu 的超載,同時配合慢函式,執行緒,鎖等知識進行優化,就能制定一套體系並且完整的啟動優化方案。

完整的閉環

上面講的都是如何進行優化,優化很重要,但並不是全部,在實際的效能優化中,我們需要做的有監控,優化,防劣化,資料收益收集等等,這些部分都做好才能形成一個完整的閉環。我一一講一下這幾個部分:

  • 監控:完整的監控應用中各項效能的指標,僅僅有指標監控是不夠的,我們還需要儘量做歸因的監控。比如記憶體監控,我們不僅僅要監控我們應用的記憶體指標,還可以還要能監控到各個業務的記憶體使用佔比,大集合,大圖片,大物件等等歸因項。並且我們的監控同樣要基於效能考慮去設計。完整的監控能讓我們更高效的發現和解決異常。
  • 優化:優化就是前面提到的,合理且充分的使用硬體資源,讓程式的表現更好。
  • 防劣化:防劣化也是有很多事情可以做的,包括建立完善的線下效能測試,線上監控的報警等。比如記憶體,我們可以線上下每天通過monkey跑記憶體洩露並提前治理,這就是防劣化。
  • 資料收益收集。學會用好A/B測試,學會關注核心價值的指標。比如我們做記憶體優化,一味的追求降低應用記憶體的佔用並不是最優,記憶體佔用的多,可能會讓我們的程式執行更快,使用者體驗更好,所以我們需要結合崩潰率,留存等等這種體驗核心價值的指標,來確定記憶體到底要不要繼續進行優化或者優化到多少。

小結

上面就是我在多年的效能優化經驗中總結出來的認知及方法論。只有瞭解了這些方法論,我們才能在進行效能優化時,如魚得水,遊刃有餘。

這篇文章也沒有介紹具體的優化方案,因為效能優化的方案通過一篇文章是介紹不完的,大家有興趣可以看看我寫的掘金小冊《Android 效能優化》,可以體系的學一學如何進行優化,上面講解的方法論,也都會在這本小冊中體現出來。