原文鏈接 http://floriankugler.com/2013/05/24/layer-trees-vs-flat-drawing-graphics-performance-across-ios-device-generations/
猶如黃油般順滑的滾動視圖,是許多iOS開發(fā)者引以為傲的地方怔檩,也是一些優(yōu)秀iOS應(yīng)用的代表。我以前并不是開發(fā)iOS平臺的,但是如今的觀點給我的感覺是启上,Loren Brichter所寫的Tweetie是許多成功的app之一,這些app推崇一種藝術(shù)店印,那就是充分利用設(shè)備所提供的每1bit的圖像性能冈在。Loren非常樂于分享他的技術(shù),通過Core Graphics把每個cell繪制成一個單獨的bitmap按摘,這樣GPU就可以做它最擅長的事情——將不透明的紋理組合起來包券。
這可以追溯到2008年纫谅,那時候iPhone 3G剛剛問世,第一代的iPhone并不是一臺你立刻會想丟棄的設(shè)備溅固。那時候高清屏也還未出現(xiàn)付秕。最近,Twitter的開發(fā)團隊發(fā)布了一篇關(guān)于讓滾動視圖順滑的文章侍郭,同樣的包含了將table view cells渲染成平面的位圖這一技術(shù)盹牧。
盡管起初的iPhone,設(shè)備CPU跟GPU的能力都極大的提高了励幼,但同時汰寓,例如高清顯示這樣的新的硬件特性又對它們提出了更高的要求。軟件方面也發(fā)生了一些變化苹粟,例如有滑,CAGradientLayer 隨著iOS 3被引進。伴隨著這些發(fā)展嵌削,我很好奇毛好,為了獲得更好的性能,那套將視圖繪制成平面的位圖的舊機制是否還能起作用苛秕。
在這篇文章里肌访,我將展示一個簡單的示例結(jié)果,該示例被分別運行在iPhone 3G艇劫,4吼驶, 4S 以及5還有iPad3,iPad mini和iPad 4店煞。根據(jù)這些結(jié)果我將討論根據(jù)不同的使用場景蟹演,究竟哪一種策略更可能有最佳的結(jié)果。
第一次渲染 & 層動畫
平順的table view滾動有兩個要素顷蟀。首先你需要能夠在1/60秒(大約是16毫秒)把一個新的table view cell顯示在屏幕上酒请,為了真正快速的滾動,也可能需要顯示多于一個cell鸣个。其次羞反,你需要能夠以大約60幀每秒的速率將已經(jīng)顯示在屏幕上的cells進行移動。上述的第一個方面根據(jù)你的代碼囤萤,跟CPU以及GPU有不同程度的關(guān)聯(lián)昼窗,然而第二個方面主要與GPU有關(guān)(假設(shè)你沒有修改已經(jīng)顯示在屏幕中的cells)。
我想要以各自獨立的形式來測試所有圖像性能的方面阁将。因此我創(chuàng)建了一個簡單的app膏秫,能夠像這樣來渲染一個view:
這個view高度100右遭,寬度在iPhone跟iPad上面都是全屏做盅。它包含了:一個不透明的漸變背景缤削,左邊是一個100x100的圖片,兩個帶有透明背景的label吹榴。我創(chuàng)建了這個view的三個版本亭敢,它們的輸出都是一樣的。
第一個版本由幾個subview構(gòu)建而成图筹。一個subview作為漸變帅刀,一個UIImageView作為圖片,兩個UILabel作為文本远剩。第二個版本由幾個sublayer構(gòu)建而成扣溺。一個CAGradientLayer作為漸變背景,一個CALayer作為圖片瓜晤,CALayer的內(nèi)容屬性是一個CGImageRef锥余,以及兩個CATextLayer。最后一個版本是用Core Graphics來繪制的平面紋理痢掠。
為了測試這些views能夠以多快的速度顯示在屏幕上驱犹,我測試了渲染5到30個(以5個遞增)這樣的視圖所需要花費的時間,每一次測試了60個樣本足画。為了測試屏幕上能夠以60幀每秒的速率進行動畫的視圖有多少個雄驹,我在我的測試應(yīng)用中遞增地渲染越來越多這樣的視圖,直到幀率掉到了60fps以下淹辞,這些視圖的動畫位置是隨機的医舆。
測量技術(shù)
你可以在GitHub上找到用來測試的工程,所以我只打算簡述一下主要的流程象缀。所有的檢測我都使用了CADisplayLink來更新屏幕彬向。在初始化CADisplayLink的時候所定義的選擇器,每秒鐘會調(diào)用60次(假設(shè)你做的事情不會花費太多時間)攻冷,同時很方便的是娃胆,你首次獲得的也是唯一的一個變量,這個跟顯示有關(guān)的東西等曼,它有一個timestamp的屬性能告訴你上一幀顯示的時間戳里烦。
為了測量新的視圖能以多快的速度顯示到屏幕上,我僅僅將一個包含了可變數(shù)量的測試視圖的父視圖移除并重建禁谦。
- (void)setupDisplayLink {
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(nextFrame:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)nextFrame:(CADisplayLink*)displayLink {
// ...
[view removeFromSuperview];
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
for (NSUInteger i = 0; i < numberOfViews; i++) {
// add new views ...
}
[self.view addSubview:view];
}
在以固定數(shù)量的視圖進行60次的迭代之后胁黑,我打印了每次循環(huán)平均花費的時間
,然后在原來的數(shù)量基礎(chǔ)上增加5個顯示視圖州泊,進入下一輪的測試丧蘸。
為了測試屏幕上的視圖能以多快的速度進行動畫,由display link所調(diào)用的方法有一點不同:
- (void)nextFrame:(CADisplayLink*)displayLink {
// ...
view.transform = CGAffineTransformMakeTranslation(self.randomNumber * 50 - 25, self.randomNumber * 50 - 25);
}
這里僅僅在每次顯示周期中設(shè)置一個不同的遥皂,隨機的平移變換作用于容器視圖力喷。由于
設(shè)置這樣的變換并不會阻礙display link在16毫秒之后調(diào)用這個方法刽漂,即使這導(dǎo)致GPU操作需要花更長的時間,我用OpenGL ES驅(qū)動測試了實際的幀率弟孟,并在數(shù)秒之后平均這個數(shù)值贝咙。
首次渲染的性能
iPhone
下面的圖表顯示了在iPhone 3G,4拂募,4s以及5上面庭猩,渲染一個測試視圖到屏幕中所花費的平均時間。每一組的三個豎狀條代表了不同版本的視圖:由subview構(gòu)成陈症,由sublayer構(gòu)成蔼水,以及Core Graphics繪制。
隨著設(shè)備更新?lián)Q代录肯,使用subviews跟sublayers來渲染視圖的時間也改善了徙缴,與此不同的是,我們觀察到在iPhone4上嘁信,使用繪制使得性能大幅度降低了于样。高清屏導(dǎo)致有4倍數(shù)量的像素需要被繪制。由于使用Core Graphics繪制是CPU的任務(wù)潘靖,CPU的性能提高并不足以支持新一代的顯示需求穿剖。在這個例子中,iPhone 5是使用Core Graphics繪制這個方法首次在性能上超過了iPhone 3GS卦溢。
下面的線性圖顯示了在每個設(shè)備中糊余,每一次渲染5到30個視圖所需要花費的時間。我想要在此強調(diào)单寂,對于iPhone 3GS贬芥,使用Core Graphics繪制視圖的性能跟使用sublayers相比是很差的。進一步宣决,你可以看到對于iPhone 4s蘸劈,使用sublayers可以60幀每秒地渲染10個視圖,但是subviews只能渲染5個尊沸,這是由于UIViews是通過CALayers來包裝的威沫,所以會增加CPU額外的工作。在iPhone5的時候洼专,這個數(shù)量分別增加了15個(sublayers)以及10個(subviews)棒掠。
iPad
對于iPad來說,情況跟我們所看到的iPhone的情況是類似的屁商。
然而烟很,由于iPad需要比iPhone繪制更多的像素,我們甚至可以看到在非高清的iPad mini(跟iPad 2應(yīng)該很類似)上,跟使用subviews還有sublayers相比雾袱,使用Core Graphics繪制的性能要更差一些恤筛。
iPad 3在此對比中相當明顯。使用繪制的性能相當糟糕谜酒。高清屏幕是優(yōu)秀的,但是很顯然硬件還沒為此準備好妻枕。
動畫的性能
現(xiàn)在讓我們來看一下不同代的iPhone以及iPad在動畫性能上的差別僻族,例如,繞著屏幕移動一個已存在的視圖屡谐。對于這些測試述么,我將僅僅顯示sublayers跟Core Graphics的結(jié)果差別,因為由subview構(gòu)成的結(jié)果和由sublayer構(gòu)成的是基本一致的愕掏。
iPhone
下面的圖表顯示了能夠順滑地以60幀每秒在每個設(shè)備上進行動畫的視圖的數(shù)量度秘。正如預(yù)期中的,在這個測試里饵撑,繪制成平面位圖明顯優(yōu)于由數(shù)個sublayer構(gòu)成的情況(大概是4:1剑梳,8:1,7:1跟7:1)滑潘,由于對于每個視圖垢乙,GPU僅僅只需要移動一個不透明的紋理。
然而语卤,我仍然想指出追逮,盡管iPhone 3GS能夠以60幀每秒,推動大約20個由sublayers構(gòu)成的測試視圖粹舵。對于這么小的屏幕來說這已經(jīng)是相當多的視圖了钮孵。
iPad
在iPad上,繪制成平面位圖跟由sublayer構(gòu)成眼滤,它們之間性能的差別更加巨大巴席。iPad mini的比例大概是7:1,這跟我們在最新一代iPhone上面所見類似诅需。但是retina ipad 3的比例是15:1情妖,iPad 4 更加厲害。
iPad較大的屏幕尺寸使得它一次顯示的視圖比iPhone要多诱担,要么是更大的table views毡证,要么是在使用grid views的時候。由于跟同期的iPhone相比蔫仙,它的大尺寸屏幕讓GPU(CPU也是如此)顯得性能不夠強大料睛。這經(jīng)常使得iPad成為了更多時候被吐槽性能的設(shè)備。
結(jié)論
隨著高清屏幕在iPhone和更大尺寸的iPad上面的應(yīng)用,使用Core Graphics將視圖繪制成平面位圖的性能特性發(fā)生了巨大的改變恤煞。Core Graphics繪制是基于CPU的操作屎勘,繪制4倍數(shù)量的像素意味著CPU更多的負擔。此外居扒,在不同代的設(shè)備之間概漱,跟GPU性能相比,CPU的性能增加的并不多喜喂。
在高清屏的iPad跟iPhone上瓤摧,與使用Core Graphics手動繪制性能敏感的視圖這種做法相比,將視圖拆解成sublayers通常會是更好的選擇玉吁。不過我需要兩個前提來保證這個觀點:首先照弥,這個視圖需要有可以用layer來表示的元素(統(tǒng)一顏色的區(qū)域,漸變进副,圖片)这揣。只有那樣你才會在不同的技術(shù)之間察覺到有意義的區(qū)別。其次影斑,GPU仍然需要能夠流暢地對layer tree進行動畫给赞。這取決于視圖本身的復(fù)雜度以及同時顯示在屏幕中的視圖數(shù)量。
如果GPU不能以每秒60幀的速度對layer tree進行動畫矫户,你將可能從視圖層級的扁平化中受益塞俱。但是并不需要一定將整個視圖都繪制到一個位圖中。你也可以在手動繪制的同時使用layers來對需要扁平化的部分進行實驗吏垮,例如一個漸變的背景以及一個圖像障涯。在這個例子中,我們可以將所有l(wèi)abel都繪制到一個layer膳汪,同時保持漸變以及圖片在各自的層級中唯蝶。這會使得GPU需要處理的層級數(shù)量減少25%,CPU沒有了渲染大幅背景區(qū)域這一負擔遗嗽。另一種觀點是粘我,試著設(shè)置視圖layer的shouldRasterize為YES。
在一些情況下這將改善動畫的性能痹换,同時付出的代價是渲染的時間征字。
在這個示例中,對于subview跟sublayer娇豫,我使用了CAGradientLayer創(chuàng)建了漸變背景匙姜,對于平面繪制,我使用了CGContextDrawLinearGradient冯痢。使用一張圖片來作為漸變(或者將漸變預(yù)渲染到一個位圖上下文)會相對快一些氮昧,但是并不會改變不同視圖實現(xiàn)方法之間的性能差別框杜。如果你使用一張圖片,一定要注意使用視圖合適的尺寸(這樣它就不用被縮放)袖肥,@1x跟@2x版本都同時提供了咪辱。否則,性能將會比自己繪制漸變來的差椎组。
最后油狂,你需要評估你特殊的使用情況,然后對你所支持的設(shè)備找到最佳的權(quán)衡方案寸癌。
我高度推薦使用CADisplayLink技術(shù)來測試你可以多快地將視圖顯示到屏幕中专筷,以及每秒可以多頻繁地對它們進行動畫。這很好地分離了圖像性能的兩個方面灵份,跟僅僅手動滾動table view相比仁堪,它給了你更多可靠的數(shù)據(jù)哮洽。
結(jié)論:隨著高清屏的引進填渠,自定義繪制可能不再是最佳的解決方案,使用sublayers來構(gòu)成視圖成為了一個真正的可選方案鸟辅。