12.1. CPU VS GPU

2018-02-24 15:07 更新

CPU VS GPU

????關(guān)于繪圖和動(dòng)畫有兩種處理的方式:CPU(中央處理器)和GPU(圖形處理器)。在現(xiàn)代iOS設(shè)備中,都有可以運(yùn)行不同軟件的可編程芯片,但是由于歷史原因,我們可以說(shuō)CPU所做的工作都在軟件層面,而GPU在硬件層面。

????總的來(lái)說(shuō),我們可以用軟件(使用CPU)做任何事情,但是對(duì)于圖像處理,通常用硬件會(huì)更快,因?yàn)镚PU使用圖像對(duì)高度并行浮點(diǎn)運(yùn)算做了優(yōu)化。由于某些原因,我們想盡可能把屏幕渲染的工作交給硬件去處理。問(wèn)題在于GPU并沒有無(wú)限制處理性能,而且一旦資源用完的話,性能就會(huì)開始下降了(即使CPU并沒有完全占用)

????大多數(shù)動(dòng)畫性能優(yōu)化都是關(guān)于智能利用GPU和CPU,使得它們都不會(huì)超出負(fù)荷。于是我們首先需要知道Core Animation是如何在這兩個(gè)處理器之間分配工作的。

動(dòng)畫的舞臺(tái)

????Core Animation處在iOS的核心地位:應(yīng)用內(nèi)和應(yīng)用間都會(huì)用到它。一個(gè)簡(jiǎn)單的動(dòng)畫可能同步顯示多個(gè)app的內(nèi)容,例如當(dāng)在iPad上多個(gè)程序之間使用手勢(shì)切換,會(huì)使得多個(gè)程序同時(shí)顯示在屏幕上。在一個(gè)特定的應(yīng)用中用代碼實(shí)現(xiàn)它是沒有意義的,因?yàn)樵趇OS中不可能實(shí)現(xiàn)這種效果(App都是被沙箱管理,不能訪問(wèn)別的視圖)。

????動(dòng)畫和屏幕上組合的圖層實(shí)際上被一個(gè)單獨(dú)的進(jìn)程管理,而不是你的應(yīng)用程序。這個(gè)進(jìn)程就是所謂的渲染服務(wù)。在iOS5和之前的版本是SpringBoard進(jìn)程(同時(shí)管理著iOS的主屏)。在iOS6之后的版本中叫做BackBoard。

????當(dāng)運(yùn)行一段動(dòng)畫時(shí)候,這個(gè)過(guò)程會(huì)被四個(gè)分離的階段被打破:

  • 布局?- 這是準(zhǔn)備你的視圖/圖層的層級(jí)關(guān)系,以及設(shè)置圖層屬性(位置,背景色,邊框等等)的階段。

  • 顯示?- 這是圖層的寄宿圖片被繪制的階段。繪制有可能涉及你的-drawRect:-drawLayer:inContext:方法的調(diào)用路徑。

  • 準(zhǔn)備?- 這是Core Animation準(zhǔn)備發(fā)送動(dòng)畫數(shù)據(jù)到渲染服務(wù)的階段。這同時(shí)也是Core Animation將要執(zhí)行一些別的事務(wù)例如解碼動(dòng)畫過(guò)程中將要顯示的圖片的時(shí)間點(diǎn)。

  • 提交?- 這是最后的階段,Core Animation打包所有圖層和動(dòng)畫屬性,然后通過(guò)IPC(內(nèi)部處理通信)發(fā)送到渲染服務(wù)進(jìn)行顯示。

????但是這些僅僅階段僅僅發(fā)生在你的應(yīng)用程序之內(nèi),在動(dòng)畫在屏幕上顯示之前仍然有更多的工作。一旦打包的圖層和動(dòng)畫到達(dá)渲染服務(wù)進(jìn)程,他們會(huì)被反序列化來(lái)形成另一個(gè)叫做渲染樹的圖層樹(在第一章“圖層樹”中提到過(guò))。使用這個(gè)樹狀結(jié)構(gòu),渲染服務(wù)對(duì)動(dòng)畫的每一幀做出如下工作:

  • 對(duì)所有的圖層屬性計(jì)算中間值,設(shè)置OpenGL幾何形狀(紋理化的三角形)來(lái)執(zhí)行渲染

  • 在屏幕上渲染可見的三角形

????所以一共有六個(gè)階段;最后兩個(gè)階段在動(dòng)畫過(guò)程中不停地重復(fù)。前五個(gè)階段都在軟件層面處理(通過(guò)CPU),只有最后一個(gè)被GPU執(zhí)行。而且,你真正只能控制前兩個(gè)階段:布局和顯示。Core Animation框架在內(nèi)部處理剩下的事務(wù),你也控制不了它。

????這并不是個(gè)問(wèn)題,因?yàn)樵诓季趾惋@示階段,你可以決定哪些由CPU執(zhí)行,哪些交給GPU去做。那么改如何判斷呢?

GPU相關(guān)的操作

????GPU為一個(gè)具體的任務(wù)做了優(yōu)化:它用來(lái)采集圖片和形狀(三角形),運(yùn)行變換,應(yīng)用紋理和混合然后把它們輸送到屏幕上?,F(xiàn)代iOS設(shè)備上可編程的GPU在這些操作的執(zhí)行上又很大的靈活性,但是Core Animation并沒有暴露出直接的接口。除非你想繞開Core Animation并編寫你自己的OpenGL著色器,從根本上解決硬件加速的問(wèn)題,那么剩下的所有都還是需要在CPU的軟件層面上完成。

????寬泛的說(shuō),大多數(shù)CALayer的屬性都是用GPU來(lái)繪制。比如如果你設(shè)置圖層背景或者邊框的顏色,那么這些可以通過(guò)著色的三角板實(shí)時(shí)繪制出來(lái)。如果對(duì)一個(gè)contents屬性設(shè)置一張圖片,然后裁剪它 - 它就會(huì)被紋理的三角形繪制出來(lái),而不需要軟件層面做任何繪制。

????但是有一些事情會(huì)降低(基于GPU)圖層繪制,比如:

  • 太多的幾何結(jié)構(gòu) - 這發(fā)生在需要太多的三角板來(lái)做變換,以應(yīng)對(duì)處理器的柵格化的時(shí)候?,F(xiàn)代iOS設(shè)備的圖形芯片可以處理幾百萬(wàn)個(gè)三角板,所以在Core Animation中幾何結(jié)構(gòu)并不是GPU的瓶頸所在。但由于圖層在顯示之前通過(guò)IPC發(fā)送到渲染服務(wù)器的時(shí)候(圖層實(shí)際上是由很多小物體組成的特別重量級(jí)的對(duì)象),太多的圖層就會(huì)引起CPU的瓶頸。這就限制了一次展示的圖層個(gè)數(shù)(見本章后續(xù)“CPU相關(guān)操作”)。

  • 重繪 - 主要由重疊的半透明圖層引起。GPU的填充比率(用顏色填充像素的比率)是有限的,所以需要避免重繪(每一幀用相同的像素填充多次)的發(fā)生。在現(xiàn)代iOS設(shè)備上,GPU都會(huì)應(yīng)對(duì)重繪;即使是iPhone 3GS都可以處理高達(dá)2.5的重繪比率,并任然保持60幀率的渲染(這意味著你可以繪制一個(gè)半的整屏的冗余信息,而不影響性能),并且新設(shè)備可以處理更多。

  • 離屏繪制 - 這發(fā)生在當(dāng)不能直接在屏幕上繪制,并且必須繪制到離屏圖片的上下文中的時(shí)候。離屏繪制發(fā)生在基于CPU或者是GPU的渲染,或者是為離屏圖片分配額外內(nèi)存,以及切換繪制上下文,這些都會(huì)降低GPU性能。對(duì)于特定圖層效果的使用,比如圓角,圖層遮罩,陰影或者是圖層光柵化都會(huì)強(qiáng)制Core Animation提前渲染圖層的離屏繪制。但這不意味著你需要避免使用這些效果,只是要明白這會(huì)帶來(lái)性能的負(fù)面影響。

  • 過(guò)大的圖片 - 如果視圖繪制超出GPU支持的2048x2048或者4096x4096尺寸的紋理,就必須要用CPU在圖層每次顯示之前對(duì)圖片預(yù)處理,同樣也會(huì)降低性能。

CPU相關(guān)的操作

????大多數(shù)工作在Core Animation的CPU都發(fā)生在動(dòng)畫開始之前。這意味著它不會(huì)影響到幀率,所以很好,但是他會(huì)延遲動(dòng)畫開始的時(shí)間,讓你的界面看起來(lái)會(huì)比較遲鈍。

????以下CPU的操作都會(huì)延遲動(dòng)畫的開始時(shí)間:

  • 布局計(jì)算 - 如果你的視圖層級(jí)過(guò)于復(fù)雜,當(dāng)視圖呈現(xiàn)或者修改的時(shí)候,計(jì)算圖層幀率就會(huì)消耗一部分時(shí)間。特別是使用iOS6的自動(dòng)布局機(jī)制尤為明顯,它應(yīng)該是比老版的自動(dòng)調(diào)整邏輯加強(qiáng)了CPU的工作。

  • 視圖懶加載 - iOS只會(huì)當(dāng)視圖控制器的視圖顯示到屏幕上時(shí)才會(huì)加載它。這對(duì)內(nèi)存使用和程序啟動(dòng)時(shí)間很有好處,但是當(dāng)呈現(xiàn)到屏幕上之前,按下按鈕導(dǎo)致的許多工作都會(huì)不能被及時(shí)響應(yīng)。比如控制器從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù),或者視圖從一個(gè)nib文件中加載,或者涉及IO的圖片顯示(見后續(xù)“IO相關(guān)操作”),都會(huì)比CPU正常操作慢得多。

  • Core Graphics繪制 - 如果對(duì)視圖實(shí)現(xiàn)了-drawRect:方法,或者CALayerDelegate-drawLayer:inContext:方法,那么在繪制任何東西之前都會(huì)產(chǎn)生一個(gè)巨大的性能開銷。為了支持對(duì)圖層內(nèi)容的任意繪制,Core Animation必須創(chuàng)建一個(gè)內(nèi)存中等大小的寄宿圖片。然后一旦繪制結(jié)束之后,必須把圖片數(shù)據(jù)通過(guò)IPC傳到渲染服務(wù)器。在此基礎(chǔ)上,Core Graphics繪制就會(huì)變得十分緩慢,所以在一個(gè)對(duì)性能十分挑剔的場(chǎng)景下這樣做十分不好。

  • 解壓圖片 - PNG或者JPEG壓縮之后的圖片文件會(huì)比同質(zhì)量的位圖小得多。但是在圖片繪制到屏幕上之前,必須把它擴(kuò)展成完整的未解壓的尺寸(通常等同于圖片寬 x 長(zhǎng) x 4個(gè)字節(jié))。為了節(jié)省內(nèi)存,iOS通常直到真正繪制的時(shí)候才去解碼圖片(14章“圖片IO”會(huì)更詳細(xì)討論)。根據(jù)你加載圖片的方式,第一次對(duì)圖層內(nèi)容賦值的時(shí)候(直接或者間接使用UIImageView)或者把它繪制到Core Graphics中,都需要對(duì)它解壓,這樣的話,對(duì)于一個(gè)較大的圖片,都會(huì)占用一定的時(shí)間。

????當(dāng)圖層被成功打包,發(fā)送到渲染服務(wù)器之后,CPU仍然要做如下工作:為了顯示屏幕上的圖層,Core Animation必須對(duì)渲染樹種的每個(gè)可見圖層通過(guò)OpenGL循環(huán)轉(zhuǎn)換成紋理三角板。由于GPU并不知曉Core Animation圖層的任何結(jié)構(gòu),所以必須要由CPU做這些事情。這里CPU涉及的工作和圖層個(gè)數(shù)成正比,所以如果在你的層級(jí)關(guān)系中有太多的圖層,就會(huì)導(dǎo)致CPU沒一幀的渲染,即使這些事情不是你的應(yīng)用程序可控的。

IO相關(guān)操作

????還有一項(xiàng)沒涉及的就是IO相關(guān)工作。上下文中的IO(輸入/輸出)指的是例如閃存或者網(wǎng)絡(luò)接口的硬件訪問(wèn)。一些動(dòng)畫可能需要從山村(甚至是遠(yuǎn)程URL)來(lái)加載。一個(gè)典型的例子就是兩個(gè)視圖控制器之間的過(guò)渡效果,這就需要從一個(gè)nib文件或者是它的內(nèi)容中懶加載,或者一個(gè)旋轉(zhuǎn)的圖片,可能在內(nèi)存中尺寸太大,需要?jiǎng)討B(tài)滾動(dòng)來(lái)加載。

????IO比內(nèi)存訪問(wèn)更慢,所以如果動(dòng)畫涉及到IO,就是一個(gè)大問(wèn)題??偟膩?lái)說(shuō),這就需要使用聰敏但尷尬的技術(shù),也就是多線程,緩存和投機(jī)加載(提前加載當(dāng)前不需要的資源,但是之后可能需要用到)。這些技術(shù)將會(huì)在第14章中討論。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)