先來談談CPU和GPU
- 在螢幕成像的過程中,CPU和GPU起著至關重要的作用
- CPU( Central Processing Unit, 中央處理器)就是機器的“大腦”,也是佈局謀略、發號施令、控制行動的“總司令官”。
- CPU的結構主要包括運算器(ALU, Arithmetic and Logic Unit)、控制單元(CU, Control Unit)、暫存器(Register)、快取記憶體器(Cache)和它們之間通 訊的資料、控制及狀態的匯流排。
- GPU全稱為Graphics Processing Unit,中文為圖形處理器,就如它的名字一樣,GPU最初是用在個人電腦、工作站、遊戲機和一些移動裝置(如平板電腦、智慧手機等)上執行繪圖運算工作的微處理器。
- 為什麼GPU特別擅長處理影象資料呢?這是因為影象上的每一個畫素點都有被處理的需要,而且每個畫素點處理的過程和方式都十分相似,也就成了GPU的天然溫床。
螢幕成像原理
卡頓產生的原因
在Sync訊號到來後,系統圖形服務會通過CADisplayLink等機制通知App,App主執行緒開始在CPU中計算顯示內容,比如檢視的建立,佈局計算,圖片解碼,文字繪製等。隨後CPU會將計算好的內容提交到GPU去,由GPU進行交換,合成,渲染。隨後GPU會把渲染結果提交到幀緩衝區,等待下一次VSync訊號(垂直同步訊號)到來時顯示到螢幕上。由於垂直同步機制,如果在一個VSync時間內,CPU或者GPU沒有完成內容提交,則那一幀就會被丟棄,等待下一次機會再顯示,而這時螢幕因為沒有新的重新整理,會保留之前的內容不變。這就造成了卡頓。- 按照60FPS的刷幀率,每隔16ms就會有一次VSync訊號
卡頓優化 -CPU
- 儘量用輕量級的物件,比如用不到事件處理的地方,可以考慮使用CALayer取代UIView
- 不要頻繁地呼叫UIView的相關屬性,比如frame、bounds、transform等屬性,儘量減少不必要的修改
- 儘量提前計算好佈局,在有需要時一次性調整對應的屬性,不要多次修改屬性
- Autolayout會比直接設定frame消耗更多的CPU資源
- 圖片的size最好剛好跟UIImageView的size保持一致
- 控制一下執行緒的最大併發數量
- 儘量把耗時的操作放到子執行緒
卡頓優化 -GPU
- 儘量避免短時間內大量圖片的顯示,儘可能將多張圖片合成一張進行顯示
- 儘量減少檢視數量和層次
- 減少透明的檢視(alpha<1),不透明的就設定opaque為YES
- 儘量避免出現離屏渲染
離屏渲染
- 在OpenGL中,GPU有2種渲染方式
1.On-Screen Rendering:當前螢幕渲染,在當前用於顯示的螢幕緩衝區進行渲染操作
2.Off-Screen Rendering:離屏渲染,在當前螢幕緩衝區以外新開闢一個緩衝區進行渲染操作 - 離屏渲染消耗效能的原因
1.需要建立新的緩衝區
2.離屏渲染的整個過程,需要多次切換上下文環境,先是從當前螢幕(On-Screen)切換到離屏(Off-Screen);等到離屏渲染結束以後,將離屏緩衝區的渲染結果顯示到螢幕上,又需要將上下文環境從離屏切換到當前螢幕 - 哪些操作會觸發離屏渲染?
1.光柵化,layer.shouldRasterize = YES
2.遮罩,layer.mask
3.圓角,同時設定layer.masksToBounds = YES、layer.cornerRadius大於0 考慮通過CoreGraphics繪製裁剪圓角,或者叫美工提供圓角圖片
4.陰影,layer.shadowXXX 如果設定了layer.shadowPath就不會產生離屏渲染
耗電優化
耗電的主要來源?
- CPU處理,Processing
- 網路,Networking
- 定位,Location
- 影象,Graphics
1. 定位優化
- 如果只是需要快速確定使用者位置,最好用CLLocationManager的requestLocation方法。定位完成後,會自動讓定位硬體斷電
- 如果不是導航應用,儘量不要實時更新位置,定位完畢就關掉定位服務
- 儘量降低定位精度,比如儘量不要使用精度最高的kCLLocationAccuracyBest
- 需要後臺定位時,儘量設定pausesLocationUpdatesAutomatically為YES,如果使用者不太可能移動的時候系統會自動暫停位置更新
- 儘量不要使用startMonitoringSignificantLocationChanges,優先考慮startMonitoringForRegion:
APP啟動優化
先來看app啟動流程
APP的啟動可以分為2種
1、冷啟動(Cold Launch):從零開始啟動APP
2、熱啟動(Warm Launch):APP已經在記憶體中,在後臺存活著,再次點選圖示啟動APP
APP啟動時間的優化,主要是針對冷啟動進行優化
- 通過新增環境變數可以列印出APP的啟動時間分析(Edit scheme -> Run -> Arguments)
1、DYLD_PRINT_STATISTICS設定為1
2、如果需要更詳細的資訊,那就將DYLD_PRINT_STATISTICS_DETAILS設定為1
APP的冷啟動概括為三大階段
-
dyld
,Apple的動態連結器,可以用來裝載Mach-O檔案(可執行檔案、動態庫等)
啟動APP時,dyld所做的事情有
1.裝載APP的可執行檔案,同時會遞迴載入所有依賴的動態庫
2.當dyld把可執行檔案、動態庫都裝載完畢後,會通知Runtime進行下一步的處理 -
runtime
啟動APP時,runtime所做的事情有
1.呼叫map_images進行可執行檔案內容的解析和處理
2.在load_images中呼叫call_load_methods,呼叫所有Class和Category的+load方法
3.進行各種objc結構的初始化(註冊Objc類 、初始化類物件等等)
4.呼叫C++靜態初始化器和__attribute__((constructor))修飾的函式
到此為止,可執行檔案和動態庫中所有的符號(Class,Protocol,Selector,IMP,…)都已經按格式成功載入到記憶體中,被runtime 所管理 -
main
1.APP的啟動由dyld主導,將可執行檔案載入到記憶體,順便載入所有依賴的動態庫
2.並由runtime負責載入成objc定義的結構
3.所有初始化工作結束後,dyld就會呼叫main函式
4.接下來就是UIApplicationMain函式,AppDelegate的application:didFinishLaunchingWithOptions:方法
優化方案
一、dyld
- 減少動態庫、合併一些動態庫(定期清理不必要的動態庫)
- 減少Objc類、分類的數量、減少Selector數量(定期清理不必要的類、分類)
- 減少C++虛擬函式數量
- Swift儘量使用struct
二、runtime
用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++靜態構造器、ObjC的+load
三、main
- 在不影響使用者體驗的前提下,儘可能將一些操作延遲,不要全部都放在finishLaunching方法中
- 按需載入
後面會分享自己的優化過程
- Follow: github.com/sallenhando…
- Source: slimsallen.com/#/detail/io…