我們平時(shí)在做業(yè)務(wù)的時(shí)候很少會(huì)有性能上的瓶頸,偶爾產(chǎn)生的UI層的卡頓也是我們對(duì)程序結(jié)構(gòu)設(shè)計(jì)的不合理產(chǎn)生的。但是性能優(yōu)化卻是一個(gè)好的程序員所應(yīng)具備的兆览,而且在某些特殊的場(chǎng)景下,我們還要有能力去解決很多極限的性能問(wèn)題塞关,這里就來(lái)聊聊高性能計(jì)算方面的問(wèn)題抬探。
首先我們將這類問(wèn)題分為兩種:
- 減少計(jì)算量
- 加快單位時(shí)間的計(jì)算量
減少計(jì)算量
對(duì)于減少計(jì)算量來(lái)說(shuō),大家都是最熟悉的帆赢,也是平時(shí)碰到的最多的方法小压。
算法優(yōu)化
最常見的要屬算法優(yōu)化,比如查找算法椰于,用二分法代替順序查找怠益,排序用快速排序代替冒泡排序。
另一個(gè)比較典型的例子就是高斯模糊算法瘾婿,將一次二維矩陣的乘法轉(zhuǎn)化為兩次一維矩陣的乘法溉痢,雖然看似改變很小,但是在整個(gè)圖片的計(jì)算量上出入非常的大憋他。以3*3
大小的矩陣來(lái)看孩饼,一次二維矩陣乘法需要9
次浮點(diǎn)乘法,而兩次一維矩陣僅需要2*3
次浮點(diǎn)乘法竹挡,如果是6*6
的矩陣镀娶,這個(gè)比例將會(huì)達(dá)到36/12
。
數(shù)據(jù)結(jié)構(gòu)優(yōu)化
在我們需要更高性能的時(shí)候揪罕,我們不太可能一個(gè)方案能夠滿足所有的情況梯码,往往需要特定的場(chǎng)景使用特定的方案。
在隨機(jī)讀取遠(yuǎn)大于隨機(jī)寫入的時(shí)候好啰,數(shù)組的確是一個(gè)好方案轩娶,但是如果隨機(jī)寫入遠(yuǎn)大于隨機(jī)讀取的時(shí)候,鏈表的性能就會(huì)優(yōu)于數(shù)組框往。如果讀取和寫入都比較頻繁鳄抒,那么樹的結(jié)構(gòu)可能會(huì)是你的首選,根據(jù)所需不同可以采用平衡二叉樹椰弊,B/B+樹许溅,跳躍鏈表等。
如果需要大量寫入磁盤秉版,順序的讀取贤重,比如日志系統(tǒng)這種,那么Google的LevelDB也是一個(gè)非常好的選擇清焕。
總之并蝗,根據(jù)需要來(lái)選擇適合的數(shù)據(jù)結(jié)構(gòu)也能大量的提升性能祭犯。
空間換取時(shí)間
要知道,很多的計(jì)算是重復(fù)的滚停,而這些重復(fù)的計(jì)算可能會(huì)消耗我們大量的時(shí)間沃粗,那么我們?cè)诳臻g充足的情況下,何不緩存這一部分的數(shù)據(jù)的铐刘?
其實(shí)很多的第三方庫(kù)都使用了這種思想的陪每,索引
也可以認(rèn)為是這種思想。這里舉個(gè)簡(jiǎn)單的例子镰吵。
在圖片處理的過(guò)程中檩禾,卷積的計(jì)算量是非常大的,而乘法占了很大一部分比例疤祭。而根據(jù)我的了解盼产,ARM在處理乘法的時(shí)候,可能需要6個(gè)時(shí)鐘周期勺馆,而加法只需要一個(gè)戏售,如果能將乘法的結(jié)果緩存下來(lái),通過(guò)加法訪問(wèn)是不是一種可行的方案呢草穆?一個(gè)顏色的色值只有0~255的區(qū)間灌灾,是一個(gè)可以窮舉的范圍,所以緩存這部分結(jié)果悲柱,通過(guò)偏移量(也就是加法)來(lái)訪問(wèn)結(jié)果锋喜,是否也可以減少計(jì)算時(shí)間呢。
這種方法一般都會(huì)有一個(gè)閾值豌鸡,超過(guò)這個(gè)閾值之后才會(huì)有明顯的性能差距嘿般,所以在使用之前需要評(píng)估好自己的場(chǎng)景是否會(huì)處于優(yōu)化的閾值內(nèi)。
低等語(yǔ)言實(shí)現(xiàn)
很多高級(jí)語(yǔ)言在實(shí)現(xiàn)的時(shí)候涯冠,會(huì)附加一部分高級(jí)語(yǔ)言的特性炉奴,比如內(nèi)存回收機(jī)制等,越是底層的語(yǔ)言我們?cè)绞悄軌蚩刂破溆?jì)算量蛇更。
極端的例子就是采用匯編方案瞻赶,這樣能夠最為極限的控制其性能。當(dāng)然這樣的問(wèn)題是兼容性問(wèn)題械荷,需要為多個(gè)平臺(tái)分別寫匯編代碼共耍。其中OpenCV中的部分計(jì)算過(guò)程就是采用了匯編語(yǔ)言實(shí)現(xiàn)的。
這種方案對(duì)于其他高級(jí)語(yǔ)言(JAVA吨瞎,JS等),采用C來(lái)實(shí)現(xiàn)其底層穆咐,或許是個(gè)好的嘗試颤诀。但是使用匯編這種方法雖然有效字旭,但是不太建議,畢竟在晦澀程度和兼容性上考慮崖叫,帶來(lái)的性價(jià)比并不高遗淳,只有在極少數(shù)的特定情況下,才會(huì)考慮心傀。
加快單位時(shí)間的計(jì)算量
除了從計(jì)算量上來(lái)優(yōu)化屈暗,我們還可以通過(guò)一些其他手段,包括更優(yōu)秀的硬件來(lái)幫助我們脂男。
并行計(jì)算
目前的大部分CPU都不是單核的了养叛,包括很多移動(dòng)設(shè)備上的。但是如此高的性能我們往往不能很充分的利用宰翅。
在iOS中就有一個(gè)適合并發(fā)計(jì)算的接口dispatch_apply
弃甥。采用多線程可以最大限度的開發(fā)出CPU的潛能。
CPU層面上的浮點(diǎn)計(jì)算
很多場(chǎng)景下汁讼,都是浮點(diǎn)乘法運(yùn)算消耗了大量的時(shí)間淆攻,特別是對(duì)于ARM系列,但是新型的ARM特別設(shè)計(jì)了關(guān)于浮點(diǎn)及向量的優(yōu)化(VFP或者稱NEON)嘿架。
在iOS中瓶珊,vImage就是利用了這一特性進(jìn)行了優(yōu)化,如果有少量的圖片運(yùn)算可以使用vImage來(lái)加快我們的速度耸彪。
ARM流水線優(yōu)化
ARM系列的CPU一般都有采用流水線的架構(gòu)伞芹,因?yàn)槊恳粭l指令的執(zhí)行都需要經(jīng)過(guò)取址-譯碼-執(zhí)行
這幾個(gè)步驟,采用流水線架構(gòu)可以增加執(zhí)行效率搜囱。
但是這也有一個(gè)反例丑瞧,就是在B
相關(guān)跳轉(zhuǎn)指令的時(shí)候,需要清空流水線蜀肘,重新加載绊汹,這也會(huì)帶來(lái)一定的性能損耗。
也就是說(shuō):
int i = 0; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
i++; x += i;
比
for (int i = 0; i <= 10; i++) x += i;
效率要高很多扮宠。當(dāng)然這里有點(diǎn)吹毛求疵了西乖,一般情況下我們還是不需要考慮這部分內(nèi)容的。
GPU
其實(shí)目前來(lái)說(shuō)坛增,影響最大的莫過(guò)于圖像的處理获雕,普通的業(yè)務(wù)慢一點(diǎn)和快一點(diǎn)其實(shí)很難分辨出來(lái),也沒(méi)有必要去如此極限的優(yōu)化收捣,而圖像的計(jì)算量之龐大届案,很容易就可以感覺(jué)出來(lái),同時(shí)圖像處理速度還制約著視頻的幀率問(wèn)題罢艾,所以問(wèn)題的嚴(yán)重程度要高很多楣颠。
那么說(shuō)到圖像處理尽纽,就不得不涉及到GPU了。目前移動(dòng)端基本都支持OpenGL2.0或OpenGL3.0童漩,所以如果要跨平臺(tái)使用可以考慮OpenGL來(lái)優(yōu)化弄贿。但是如果只考慮iOS平臺(tái),讓我們來(lái)看看iOS平臺(tái)有哪些特定的利用GPU進(jìn)行優(yōu)化的方案矫膨。
GPUImage
這是一個(gè)利用OpenGL的開源庫(kù)差凹,是一個(gè)跨平臺(tái)的第三方庫(kù),里面封裝了很多濾鏡侧馅,同時(shí)也支持圖片危尿、視頻的處理,對(duì)于自定義和擴(kuò)展也比較方便施禾,是一款非常好用的開源庫(kù)脚线。
CIContext
Core Image是蘋果官方提供的一款圖像處理庫(kù),里面包含了眾多的濾鏡弥搞。其中CIContext可以指定為glContext邮绿,就是GPU環(huán)境了。一般來(lái)說(shuō)攀例,我們平時(shí)開發(fā)使用船逮,CIImage已經(jīng)足夠了。
CImage的擴(kuò)展也非常方便粤铭,有一種類似于openGL的GLSL的語(yǔ)言挖胃,KLSL。有興趣的人可以自己去研究研究梆惯。
Metal
Metal是蘋果比較新的一個(gè)庫(kù)酱鸭,專門為了替代OpenGL而做的,降低了OpenGL的學(xué)習(xí)成本垛吗。
據(jù)官方稱凹髓,加入了眾多的優(yōu)化。其中一個(gè)比較明顯的就是怯屉,將shader的編譯過(guò)程放到了編譯期蔚舀,而不是運(yùn)行期,也就是說(shuō)锨络,比起OpenGL赌躺,少了一步glCompileShader
。
同時(shí)Metal也和iOS的特性結(jié)合的比較好羡儿,使用起來(lái)也比OpenGL簡(jiǎn)單很多礼患。當(dāng)然這些都是iOS平臺(tái)的特性,不支持跨平臺(tái)。
OpenCL
這個(gè)是對(duì)應(yīng)于mac系統(tǒng)的讶泰,其他系統(tǒng)上也有實(shí)現(xiàn)咏瑟,目前還不支持移動(dòng)端拂到。
多緩沖FrameBuffer的GPU
對(duì)于視頻這類連續(xù)的計(jì)算痪署,以上的方案已經(jīng)非常的優(yōu)秀了,但是我們還是沒(méi)有榨干機(jī)器的性能兄旬。:D
一張圖片的處理流程可以表示為上圖狼犯,CPU需要將數(shù)據(jù)參數(shù)準(zhǔn)備好,然后拷貝到GPU內(nèi)存空間领铐,然后等待GPU執(zhí)行悯森。GPU執(zhí)行完之后,需要等待CPU準(zhǔn)備好下一張圖片的數(shù)據(jù)并拷貝到GPU空間,在這之間是留了很多的空白時(shí)間的。
我們知道OpenGL并不是線程安全的由驹,也就是說(shuō)GPU空間是可以多線程同時(shí)訪問(wèn)的姿染,那么我們可以通過(guò)多個(gè)緩沖區(qū)來(lái)解決上述空白時(shí)間的問(wèn)題。
上圖就是我們想要達(dá)到的效果箍土,而下圖是我們采用3個(gè)緩沖區(qū),實(shí)際上的效果。
最后
以上是對(duì)高性能計(jì)算幾種方式的一個(gè)簡(jiǎn)單概括褥傍,具體的情況需要根據(jù)自己的實(shí)際情況來(lái)選擇。