iOS開發–性能調優記錄

CPU VS GPU

  • 關於繪圖和動畫有兩種處理的方式:CPU(中央處理器)和GPU(圖形處理器)。但是由於歷史原因,我們可以說CPU所做的工作都在軟體層面,而GPU在硬體層面

  • 對於影像處理,通常用硬體會更快,因為GPU使用影像對高度並行浮點運算做了優化,我們想儘可能把螢幕渲染的工作交給硬體去處理。問題在於GPU並沒有無限制處理性能

動畫的舞台

  • 動畫和螢幕上組合的圖層實際上被一個單獨的進程管理(iOS6之後的版本中叫做BackBoard),而不是你的應用程式。這個進程就是所謂的渲染服務

  • 當運行一段動畫時候,這個過程會被四個分離的階段被打破-應用程式之內

    • 布局 – 這是準備你的視圖/圖層的層級關係,以及設置圖層屬性(位置,背景色,邊框等等)的階段
    • 顯示 – 這是圖層的寄宿圖片被繪製的階段。繪製有可能涉及你的-drawRect:和-drawLayer:inContext:方法的調用路徑
    • 準備 – 這是Core Animation準備發送動畫數據到渲染服務的階段。這同時也是Core Animation將要執行一些別的事務例如解碼動畫過程中將要顯示的圖片的時間點
    • 提交 – 這是最後的階段,Core Animation打包所有圖層和動畫屬性,然後通過IPC(內部處理通訊)發送到渲染服務進行顯示
  • 一旦打包的圖層和動畫到達渲染服務進程,他們會被反序列化來形成另一個叫做渲染樹的圖層樹,使用這個樹狀結構,渲染服務對動畫的每一幀做出如下工作:

    • 對所有的圖層屬性計算中間值,設置OpenGL幾何形狀(紋理化的三角形)來執行渲染
    • 在螢幕上渲染可見的三角形
  • 總結:共有六個階段,最後兩個階段在動畫過程中不停地重複。前五個階段都在軟體層面處理(通過CPU),只有最後一個被GPU執行。而且,你真正只能控制前兩個階段:布局和顯示。Core Animation框架在內部處理剩下的事務,你也控制不了它.

GPU相關的操作

  • 它用來採集圖片和形狀(三角形),運行變換,應用紋理和混合然後把它們輸送到螢幕上
  • 寬泛的說,大多數CALayer的屬性都是用GPU來繪製
  • 會降低(基於GPU)圖層繪製
    • 太多的幾何結構 – 這發生在需要太多的三角板來做變換,以應對處理器的柵格化的時候。現代iOS設備的圖形晶片可以處理幾百萬個三角板,所以在Core Animation中幾何結構並不是GPU的瓶頸所在。但由於圖層在顯示之前通過IPC發送到渲染伺服器的時候(圖層實際上是由很多小物體組成的特別重量級的對象),太多的圖層就會引起CPU的瓶頸。這就限制了一次展示的圖層個數
    • 重繪 – 主要由重疊的半透明圖層引起。GPU的填充比率(用顏色填充像素的比率)是有限的,所以需要避免重繪(每一幀用相同的像素填充多次)的發生。在現代iOS設備上,GPU都會應對重繪;即使是iPhone 3GS都可以處理高達2.5的重繪比率,並任然保持60幀率的渲染(這意味著你可以繪製一個半的整屏的冗餘資訊,而不影響性能),並且新設備可以處理更多。
    • 離屏繪製 – 這發生在當不能直接在螢幕上繪製,並且必須繪製到離屏圖片的上下文中的時候。離屏繪製發生在基於CPU或者是GPU的渲染,或者是為離屏圖片分配額外記憶體,以及切換繪製上下文,這些都會降低GPU性能。對於特定圖層效果的使用,比如圓角,圖層遮罩,陰影或者是圖層光柵化都會強制Core Animation提前渲染圖層的離屏繪製。但這不意味著你需要避免使用這些效果,只是要明白這會帶來性能的負面影響。
    • 過大的圖片 – 如果視圖繪製超出GPU支援的2048×2048或者4096×4096尺寸的紋理,就必須要用CPU在圖層每次顯示之前對圖片預處理,同樣也會降低性能

CPU相關的操作

  • 延遲動畫的開始時間

    • 布局計算 – 如果你的視圖層級過於複雜,當視圖呈現或者修改的時候,計算圖層幀率就會消耗一部分時間。特別是使用iOS6的自動布局機制尤為明顯,它應該是比老版的自動調整邏輯加強了CPU的工作。
    • 視圖懶載入 – iOS只會當視圖控制器的視圖顯示到螢幕上時才會載入它。這對記憶體使用和程式啟動時間很有好處,但是當呈現到螢幕上之前,按下按鈕導致的許多工作都會不能被及時響應。比如控制器從資料庫中獲取數據,或者視圖從一個nib文件中載入,或者涉及IO的圖片顯示,都會比CPU正常操作慢得多。
    • Core Graphics繪製 – 如果對視圖實現了-drawRect:方法,或者CALayerDelegate的-drawLayer:inContext:方法,那麼在繪製任何東西之前都會產生一個巨大的性能開銷。為了支援對圖層內容的任意繪製,Core Animation必須創建一個記憶體中等大小的寄宿圖片。然後一旦繪製結束之後,必須把圖片數據通過IPC傳到渲染伺服器。在此基礎上,Core Graphics繪製就會變得十分緩慢,所以在一個對性能十分挑剔的場景下這樣做十分不好。
    • 解壓圖片– PNG或者JPEG壓縮之後的圖片文件會比同品質的點陣圖小得多。但是在圖片繪製到螢幕上之前,必須把它擴展成完整的未解壓的尺寸(通常等同於圖片寬 x 長 x 4個位元組)。為了節省記憶體,iOS通常直到真正繪製的時候才去解碼圖片。根據你載入圖片的方式,第一次對圖層內容賦值的時候(直接或者間接使用UIImageView)或者把它繪製到Core Graphics中,都需要對它解壓,這樣的話,對於一個較大的圖片,都會佔用一定的時間。
  • 當圖層被成功打包,發送到渲染伺服器之後,CPU仍然要做如下工作:為了顯示器幕上的圖層,Core Animation必須對渲染樹種的每個可見圖層通過OpenGL循環轉換成紋理三角板。由於GPU並不知曉Core Animation圖層的任何結構,所以必須要由CPU做這些事情。這裡CPU涉及的工作和圖層個數成正比,所以如果在你的層級關係中有太多的圖層,就會導致CPU沒一幀的渲染,即使這些事情不是你的應用程式可控的

IO相關操作

  • 上下文中的IO(輸入/輸出)指的是例如快閃記憶體或者網路介面的硬體訪問。一些動畫可能需要從山村(甚至是遠程URL)來載入。一個典型的例子就是兩個視圖控制器之間的過渡效果,這就需要從一個nib文件或者是它的內容中懶載入,或者一個旋轉的圖片,可能在記憶體中尺寸太大,需要動態滾動來載入
  • IO比記憶體訪問更慢,所以如果動畫涉及到IO,就是一個大問題。總的來說,這就需要使用聰敏但尷尬的技術,也就是多執行緒,快取和投機載入

測量而非猜測

  • 模擬器運行在你的Mac上,然而Mac上的CPU往往比iOS設備要快。相反,Mac上的GPU和iOS設備的完全不一樣,模擬器不得已要在軟體層面(CPU)模擬設備的GPU,這意味著GPU相關的操作在模擬器上運行的更慢,尤其是使用CAEAGLLayer來寫一些OpenGL的程式碼時候。

  • 為了做到動畫的平滑,你需要以60FPS(幀每秒)的速度運行,以同步螢幕刷新速率。通過基於NSTimer或者CADisplayLink的動畫你可以降低到30FPS,而且效果還不錯,但是沒辦法通過Core Animation做到這點。如果不保持60FPS的速率,就可能隨機丟幀

Instruments:Profile選項來打開Instruments(在這之前,記住要把目標設置成iOS設備,而不是模擬器)

  • 時間分析器 – 用來測量被方法/函數打斷的CPU使用情況。

    • 通過執行緒分離 – 這可以通過執行的執行緒進行分組。如果程式碼被多執行緒分離的話,那麼就可以判斷到底是哪個執行緒造成了問題。
    • 隱藏系統庫 – 可以隱藏所有蘋果的框架程式碼,來幫助我們尋找哪一段程式碼造成了性能瓶頸。由於我們不能優化框架方法,所以這對定位到我們能實際修復的程式碼很有用。
    • 只顯示Obj-C程式碼 – 隱藏除了Objective-C之外的所有程式碼。大多數內部的Core Animation程式碼都是用C或者C++函數,所以這對我們集中精力到我們程式碼中顯式調用的方法就很有用
  • Core Animation – 用來調試各種Core Animation性能問題。

    • Color Blended Layers – 這個選項基於渲染程度對螢幕中的混合區域進行綠到紅的高亮(也就是多個半透明圖層的疊加)。由於重繪的原因,混合對GPU性能會有影響,同時也是滑動或者動畫幀率下降的罪魁禍首之一。
    • ColorHitsGreenandMissesRed– 當使用shouldRasterizep屬性的時候,耗時的圖層繪製會被快取,然後當做一個簡單的扁平圖片呈現。當快取再生的時候這個選項就用紅色對柵格化圖層進行了高亮。如果快取頻繁再生的話,就意味著柵格化可能會有負面的性能影響了
    • Color Copied Images – 有時候寄宿圖片的生成意味著Core Animation被強制生成一些圖片,然後發送到渲染伺服器,而不是簡單的指向原始指針。這個選項把這些圖片渲染成藍色。複製圖片對記憶體和CPU使用來說都是一項非常昂貴的操作,所以應該儘可能的避免。
    • Color Immediately– 通常Core Animation Instruments以每毫秒10次的頻率更新圖層調試顏色。對某些效果來說,這顯然太慢了。這個選項就可以用來設置每幀都更新(可能會影響到渲染性能,而且會導致幀率測量不準,所以不要一直都設置它)。
    • Color Misaligned Images – 這裡會高亮那些被縮放或者拉伸以及沒有正確對齊到像素邊界的圖片(也就是非整型坐標)。這些中的大多數通常都會導致圖片的不正常縮放,如果把一張大圖當縮略圖顯示,或者不正確地模糊影像,那麼這個選項將會幫你識別出問題所在。 –Color Offscreen-Rendered Yellow – 這裡會把那些需要離屏渲染的圖層高亮成黃色。這些圖層很可能需要用shadowPath或者shouldRasterize來優化。
    • Color OpenGL Fast Path Blue – 這個選項會對任何直接使用OpenGL繪製的圖層進行高亮。如果僅僅使用UIKit或者Core Animation的API,那麼不會有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不顯示藍色塊的話就意味著你正在強制CPU渲染額外的紋理,而不是繪製到螢幕。
    • Flash Updated Regions – 這個選項會對重繪的內容高亮成黃色(也就是任何在軟體層面使用Core Graphics繪製的圖層)。這種繪圖的速度很慢。如果頻繁發生這種情況的話,這意味著有一個隱藏的bug或者說通過增加快取或者使用替代方案會有提升性能的空間。
  • OpenGL ES驅動 – 用來調試GPU性能問題。這個工具在編寫Open GL程式碼的時候很有用,但有時也用來處理Core Animation的工作

    • Renderer Utilization – 如果這個值超過了~50%,就意味著你的動畫可能對幀率有所限制,很可能因為離屏渲染或者是重繪導致的過度混合。
    • Tiler Utilization – 如果這個值超過了~50%,就意味著你的動畫可能限制於幾何結構方面,也就是在螢幕上有太多的圖層佔用了。

推薦文章