張合花,張全法,李素曉
(鄭州大學(xué) 物理工程學(xué)院,鄭州 450001)
C語(yǔ)言程序設(shè)計(jì)和C++語(yǔ)言程序設(shè)計(jì)是許多高校開(kāi)設(shè)的計(jì)算機(jī)語(yǔ)言課程,C/C++也是廣大工程技術(shù)人員最常用的程序設(shè)計(jì)語(yǔ)言之一[1]。C/C++程序可以獲得很高的運(yùn)行速度,但是并非所有的C/C++程序一經(jīng)寫(xiě)出就具有了無(wú)可超越的運(yùn)行速度,往往還需要進(jìn)行優(yōu)化。優(yōu)化一般包括提高運(yùn)行速度和縮小可執(zhí)行文件大小兩方面的含義,本文提及的優(yōu)化是指前者。
優(yōu)化可以分為自動(dòng)優(yōu)化和人工優(yōu)化兩種方式。自動(dòng)優(yōu)化是指利用C/C++編譯器的優(yōu)化選項(xiàng)進(jìn)行優(yōu)化。一般的C/C++編譯器支持靜態(tài)自動(dòng)優(yōu)化,采用LLVM(Low Level Virtual Machine)編譯架構(gòu)時(shí)還可以支持動(dòng)態(tài)自動(dòng)優(yōu)化[2]。人工優(yōu)化是指程序員利用經(jīng)驗(yàn)和技巧進(jìn)行優(yōu)化,常用方法包括采用自增自減運(yùn)算、利用指針處理數(shù)組、合理使用內(nèi)聯(lián)函數(shù)、采用位運(yùn)算、合理使用寄存器變量以及盡量將浮點(diǎn)數(shù)運(yùn)算轉(zhuǎn)化為整數(shù)運(yùn)算等[3-7]。本文主要討論如何利用內(nèi)存拷貝函數(shù)對(duì)C/C++程序進(jìn)行人工優(yōu)化。
內(nèi)存拷貝函數(shù)memcpy()是C運(yùn)行庫(kù)中的函數(shù),在C++中仍然可以使用,使用時(shí)一般需要包含頭文件memory.h。在VC 6.0中其函數(shù)原型為
void * memcpy(void * dest,const void * src,
size_t count),
其功能是將以src為數(shù)據(jù)源首地址的內(nèi)存中的數(shù)據(jù)拷貝到以dest為目的地首地址的內(nèi)存中,要拷貝的數(shù)據(jù)量為count字節(jié)。由于采用了快速內(nèi)存拷貝技術(shù),其運(yùn)行速度比循環(huán)賦值快得多(參見(jiàn)后面的實(shí)驗(yàn)結(jié)果)。因此,在需要拷貝大量數(shù)據(jù)時(shí),利用它可以顯著提高程序的運(yùn)行速度。
在VC 6.0的幫助系統(tǒng)MSDN中僅要求這兩塊內(nèi)存不相互重疊(否則不能正確地拷貝所有數(shù)據(jù)),對(duì)于3個(gè)參數(shù)并沒(méi)有特殊要求和說(shuō)明。但是,實(shí)踐發(fā)現(xiàn)只有當(dāng)src和dest皆為字的首地址且count為字長(zhǎng)的整數(shù)倍時(shí),才可以獲得最快的運(yùn)行速度。實(shí)際應(yīng)用中可能不滿足這些條件,需要采取適當(dāng)?shù)拇胧┍M量滿足這些條件。
以利用VC 6.0編寫(xiě)實(shí)時(shí)圖像處理中經(jīng)常用到的灰度圖像空間域均值濾波函數(shù)為例,說(shuō)明如何采取措施提高程序的運(yùn)行速度。
假設(shè)濾波前的灰度圖像用g表示,寬M像素,高N像素;濾波后的圖像用b表示;所用模板寬(2K+1)像素,高(2L+1)像素。M、N、K、L皆為整數(shù)??臻g域均值濾波算法可以描述為
(1)
式中:bij—圖像b上坐標(biāo)為(i,j)的像素的灰度值;
gmn—圖像g上坐標(biāo)為(m,n)的像素的灰度值。
對(duì)于圖像中部的像素,根據(jù)式(1)求bij不存在任何問(wèn)題。對(duì)于圖像邊緣附近的像素,根據(jù)式(1)求bij將出現(xiàn)困難,因?yàn)榇藭r(shí)與模板對(duì)應(yīng)的一些像素不存在。處理此問(wèn)題的方法有多種,包括對(duì)邊緣或角上的像素設(shè)計(jì)特殊模板、假設(shè)圖像是環(huán)繞的、不處理邊緣像素等[8]。
由于涉及大量的運(yùn)算,在那些還需要實(shí)時(shí)進(jìn)行其他復(fù)雜處理的應(yīng)用中,必須采取措施來(lái)提高其運(yùn)行速度。最簡(jiǎn)單的方法是采用K=1,L=1的最小模板來(lái)減少計(jì)算量。從另一角度來(lái)看,由于模板越大圖像信息丟失越嚴(yán)重,K和L也不宜太大。此外,不處理邊緣像素也有助于減少計(jì)算量。
文獻(xiàn)[9]介紹的盒濾波方法利用上、下或左、右兩個(gè)模板位置之間的相關(guān)性來(lái)減少重復(fù)計(jì)算,從而提高其運(yùn)行速度。它利用一個(gè)含M個(gè)元素的數(shù)組B來(lái)存儲(chǔ)圖像g上以第j行為中心的(2L+1)行相應(yīng)列像素灰度值之和。計(jì)算圖像b的第一行時(shí)通過(guò)求和初始化B,計(jì)算完一行之后通過(guò)加上新的一行、減去舊的一行更新B。求bij時(shí)先計(jì)算B中以第i個(gè)元素為中心的(2K+1)個(gè)元素的和S,再將S除以(2K+1)與(2L+1)的乘積。每一行的第一個(gè)S通過(guò)求和初始化,模板向右移動(dòng)時(shí),S通過(guò)加上新的元素、減去舊的元素來(lái)更新。
對(duì)于相同大小的模板,從減少重復(fù)計(jì)算的角度看,盒濾波已經(jīng)做到了極限[6]。文獻(xiàn)[6]通過(guò)實(shí)驗(yàn)證明其運(yùn)行速度可以進(jìn)一步提高,采取的措施包括:通過(guò)指針訪問(wèn)內(nèi)存中的數(shù)據(jù),并利用自增運(yùn)算移動(dòng)指針;在K=1、L=1時(shí)利用3個(gè)臨時(shí)變量取代數(shù)組B來(lái)保存中間結(jié)果,從而將內(nèi)存訪問(wèn)轉(zhuǎn)化為寄存器訪問(wèn);將除法運(yùn)算轉(zhuǎn)化為移位運(yùn)算;等等。
如果不需要將濾波后的圖像寫(xiě)入原始圖像的數(shù)據(jù)存儲(chǔ)區(qū),文獻(xiàn)[6]所獲得的速度已經(jīng)沒(méi)有多大提升空間(除非使用更快的硬件),反之,還可以顯著提高,這需要分析此種情況下的數(shù)據(jù)拷貝操作。為了方便,假設(shè)不處理邊緣像素,并且計(jì)算機(jī)的內(nèi)存足夠用。不處理邊緣像素并不意味著丟棄邊緣像素(例如賦零),而是保留邊緣像素原來(lái)的灰度值,因?yàn)閬G棄邊緣像素對(duì)圖像的影響比較大。內(nèi)存不夠用時(shí)編程將很復(fù)雜,但這不屬于本文討論的范疇。
根據(jù)式(1)可知,求bij時(shí)所用的gmn是原始圖像上像素的灰度值,如果將求出的bij直接寫(xiě)入原始圖像數(shù)據(jù)存儲(chǔ)區(qū)而不采取措施,將嚴(yán)重影響后面的計(jì)算。處理這個(gè)問(wèn)題的方法可以分為有備份法和無(wú)備份法兩種。有備份法需要預(yù)先將原始圖像的數(shù)據(jù)備份到臨時(shí)存儲(chǔ)區(qū),然后根據(jù)臨時(shí)存儲(chǔ)區(qū)中的數(shù)據(jù)計(jì)算bij并直接寫(xiě)入原始圖像數(shù)據(jù)存儲(chǔ)區(qū)。無(wú)備份法不需要備份原始圖像數(shù)據(jù),它是根據(jù)原始圖像數(shù)據(jù)計(jì)算bij并寫(xiě)入臨時(shí)存儲(chǔ)區(qū),等到計(jì)算完成后再將臨時(shí)存儲(chǔ)區(qū)中的數(shù)據(jù)寫(xiě)入原始圖像數(shù)據(jù)存儲(chǔ)區(qū),不過(guò)此時(shí)需要分行拷貝,并拷貝到正確位置。
通過(guò)以上分析可知,如果需要將濾波后的圖像寫(xiě)入原始圖像的數(shù)據(jù)存儲(chǔ)區(qū),無(wú)論有備份法還是無(wú)備份法皆不可避免大量的數(shù)據(jù)拷貝操作(數(shù)據(jù)備份實(shí)質(zhì)上也是數(shù)據(jù)拷貝)。如果能夠?qū)崿F(xiàn)數(shù)據(jù)的快速拷貝則可以進(jìn)一步提高其運(yùn)行速度。
一般認(rèn)為,減少要拷貝的數(shù)據(jù)量可以提高數(shù)據(jù)拷貝的速度。在不處理邊緣像素時(shí),有備份法要拷貝的數(shù)據(jù)量為M×N字節(jié)(灰度數(shù)據(jù)占1個(gè)字節(jié)),無(wú)備份法為(M-2K)×(N-2L)字節(jié)。這是因?yàn)闊o(wú)備份法不需要拷貝邊緣像素的灰度值,而有備份法則很難避免備份邊緣像素的灰度值,如果采取復(fù)雜措施刻意避免,往往得不償失。因此,無(wú)備份法要拷貝的數(shù)據(jù)量比較少,有可能獲得較快的速度。
利用memcpy()函數(shù)提高數(shù)據(jù)的拷貝速度也有助于提高運(yùn)行速度。有些人不知道C語(yǔ)言的運(yùn)行庫(kù)中有專(zhuān)門(mén)用于拷貝內(nèi)存數(shù)據(jù)的函數(shù),即使知道該函數(shù),也有一部分人認(rèn)為使用它未必就好,因?yàn)橐话愠WR(shí)是函數(shù)調(diào)用需要額外的開(kāi)銷(xiāo),通常會(huì)影響運(yùn)行速度。另外,通過(guò)循環(huán)賦值實(shí)現(xiàn)數(shù)據(jù)拷貝也很容易。因此,很多人采取循環(huán)賦值方式實(shí)現(xiàn)數(shù)據(jù)拷貝。但是,當(dāng)數(shù)據(jù)拷貝操作在所有操作中所占比例較大時(shí),memcpy()函數(shù)的優(yōu)勢(shì)就顯現(xiàn)出來(lái)了。
通過(guò)以上分析似乎可以得出memcpy()函數(shù)與無(wú)備份法相結(jié)合是處理數(shù)據(jù)拷貝問(wèn)題的最佳組合的結(jié)論,其實(shí)不然。因?yàn)橐浞职l(fā)揮該函數(shù)的優(yōu)勢(shì)還需要滿足一些條件,而采用無(wú)備份法又不處理邊緣像素時(shí)無(wú)法同時(shí)滿足這些條件。這是因?yàn)椋好看我截惖臄?shù)據(jù)量(M-2K)通常不是字長(zhǎng)的整數(shù)倍;盡管系統(tǒng)分配的數(shù)據(jù)存儲(chǔ)區(qū)的首地址是字的首地址,由于(M-2K)通常不是字長(zhǎng)的整數(shù)倍,使得src通常不是字的首地址;為了拷貝到正確位置,dest通常也不是字的首地址;多次拷貝會(huì)影響速度。
采用有備份法可以同時(shí)滿足這些條件。由系統(tǒng)分配的數(shù)據(jù)存儲(chǔ)區(qū)的首地址是字的首地址,從而保證了src和dest皆是字的首地址;由攝像器件產(chǎn)生的圖像的寬度M是字長(zhǎng)的整數(shù)倍,從而保證了要拷貝的數(shù)據(jù)量是字長(zhǎng)的整數(shù)倍(對(duì)于人工隨意裁剪的圖像,存儲(chǔ)時(shí)每一行的數(shù)據(jù)量必須是字長(zhǎng)的整數(shù)倍才能夠正確顯示)。因此,memcpy()函數(shù)與有備份法相結(jié)合反而有可能獲得更快的速度,盡管不處理邊緣像素時(shí)需要拷貝的數(shù)據(jù)總量比較多。實(shí)驗(yàn)證明確實(shí)如此。
所用計(jì)算機(jī)的CPU為Intel Pentium Dual E2140處理器,主頻為1.60GHz,程序用VC 6.0編寫(xiě)。在發(fā)行版下利用VC 6.0的Build/Profile功能測(cè)試函數(shù)的運(yùn)行時(shí)間,自動(dòng)優(yōu)化選項(xiàng)為最大速度。用一段彩色視頻作為處理對(duì)象,該段視頻共3845幀,每幀圖像的M=384,N=288。
對(duì)于每幀圖像,首先采用文獻(xiàn)[5]中的方法進(jìn)行灰度化,然后調(diào)用空間域均值濾波函數(shù)對(duì)灰度圖像進(jìn)行濾波。設(shè)計(jì)空間域均值濾波函數(shù)時(shí)所用模板的K=1,L=1,并且不處理邊緣像素。為了進(jìn)行比較,采用不同方法進(jìn)行設(shè)計(jì),并分別進(jìn)行測(cè)試。為了減少Windows多任務(wù)特性的影響,每種設(shè)計(jì)都需要完成5次整段視頻的處理,每完成一次處理記錄一次空間域均值濾波函數(shù)的總運(yùn)行時(shí)間,最后計(jì)算平均總運(yùn)行時(shí)間。
設(shè)計(jì)空間域均值濾波函數(shù)時(shí)涉及數(shù)據(jù)計(jì)算與數(shù)據(jù)拷貝兩個(gè)部分。數(shù)據(jù)計(jì)算部分的功能是求bij,采用文獻(xiàn)[6]中的方法進(jìn)行快速計(jì)算。區(qū)別在于:若采用無(wú)備份法,gmn來(lái)源于原始圖像數(shù)據(jù)存儲(chǔ)區(qū),bij寫(xiě)入臨時(shí)存儲(chǔ)區(qū);若采用有備份法,gmn來(lái)源于臨時(shí)存儲(chǔ)區(qū),bij直接寫(xiě)入原始圖像數(shù)據(jù)存儲(chǔ)區(qū)。數(shù)據(jù)拷貝部分采用不同的實(shí)現(xiàn)方法,具體如下:
設(shè)計(jì)1 采用無(wú)備份法,數(shù)據(jù)拷貝通過(guò)分行循環(huán)賦值(故意這樣)實(shí)現(xiàn)。
設(shè)計(jì)2 采用無(wú)備份法,數(shù)據(jù)拷貝通過(guò)多次(必須這樣)調(diào)用memcpy()函數(shù)實(shí)現(xiàn)。
設(shè)計(jì)3 采用有備份法,數(shù)據(jù)拷貝通過(guò)多次(故意這樣)調(diào)用memcpy()函數(shù)實(shí)現(xiàn)。
設(shè)計(jì)4 采用有備份法,數(shù)據(jù)拷貝通過(guò)一次(可以這樣)調(diào)用memcpy()函數(shù)實(shí)現(xiàn)。
為了不失公允,所有設(shè)計(jì)都盡可能多地采取措施提高運(yùn)行速度。所采取的措施包括賦值縮寫(xiě)、指針訪問(wèn)、指針自增運(yùn)算和指針比較等。指針比較是指通過(guò)比較指針變量是否到達(dá)結(jié)束位置作為是否結(jié)束循環(huán)的條件,從而避免使用額外的循環(huán)變量。
實(shí)驗(yàn)結(jié)果如表1所示。根據(jù)設(shè)計(jì)2和設(shè)計(jì)1的平均總運(yùn)行時(shí)間可知,利用memcpy()函數(shù)拷貝數(shù)據(jù)比通過(guò)循環(huán)賦值快得多。根據(jù)設(shè)計(jì)3和設(shè)計(jì)2的平均總運(yùn)行時(shí)間可知,當(dāng)滿足所需條件時(shí)可以充分發(fā)揮memcpy()函數(shù)的優(yōu)勢(shì),盡管設(shè)計(jì)3比設(shè)計(jì)2每行需要多拷貝2個(gè)字節(jié),并且每幀圖像需要多拷貝2行。根據(jù)設(shè)計(jì)4和設(shè)計(jì)3的平均總運(yùn)行時(shí)間可知,整體拷貝比分行拷貝快??梢郧蟪?,設(shè)計(jì)4處理一幀圖像所用的平均時(shí)間為0.668 ms,運(yùn)行速度比設(shè)計(jì)1提高了8.3%。
表1 不同設(shè)計(jì)的總運(yùn)行時(shí)間 ms
根據(jù)上述實(shí)驗(yàn)數(shù)據(jù),設(shè)計(jì)4比設(shè)計(jì)1的運(yùn)行速度提高得似乎很有限,但是上述總運(yùn)行時(shí)間中還包含了數(shù)據(jù)計(jì)算部分所需要的時(shí)間,如果將數(shù)據(jù)計(jì)算部分注釋掉,再按照上述方法進(jìn)行測(cè)試,求得設(shè)計(jì)1~設(shè)計(jì)4數(shù)據(jù)拷貝部分的平均總運(yùn)行時(shí)間分別為375ms,224ms,188ms和142 ms,設(shè)計(jì)2~設(shè)計(jì)4比設(shè)計(jì)1的數(shù)據(jù)拷貝部分運(yùn)行速度分別提高了40 %,50 %和62 %。這充分說(shuō)明了使用memcpy()函數(shù)取代循環(huán)賦值的必要性,也說(shuō)明了正確使用該函數(shù)的重要性。
本文討論了利用memcpy()函數(shù)對(duì)C/C++程序進(jìn)行人工優(yōu)化的問(wèn)題。以設(shè)計(jì)圖像處理中常用的空間域均值濾波函數(shù)為例,討論了如何通過(guò)分析尋找進(jìn)一步提高程序運(yùn)行速度的途徑,并通過(guò)實(shí)驗(yàn)證明利用本文所述方法可以顯著提高程序的運(yùn)行速度。這種優(yōu)化程序的思想方法可以為工程技術(shù)人員和有關(guān)人員設(shè)計(jì)需要快速處理大量數(shù)據(jù)的C/C++程序提供參考。
[1]張合花,張全法,齊永奇.準(zhǔn)確使用C/C++中等于運(yùn)算符的研究[J].中州大學(xué)學(xué)報(bào),2016,33(2):112-115.
[2]朱曉珺,李冬梅.C/C++程序的運(yùn)行時(shí)優(yōu)化研究[J].軟件導(dǎo)刊,2009,8(4):60-62.
[3]唐娟.合理提高C/C++程序效率的方法[J].孝感學(xué)院學(xué)報(bào),2006,26(3):68-70.
[4]張志軍,孫志輝.基于VC平臺(tái)的彩色圖像的灰度化技術(shù)[J].自動(dòng)化技術(shù)與應(yīng)用,2005,24(1):61-63.
[5]張全法,楊海彬,任朝棟,等.彩色圖像的快速高保真灰度化方法研究[J].鄭州大學(xué)學(xué)報(bào)(理學(xué)版),2011,43(3):66-69.
[6]張全法,費(fèi)光彥,王慶峰,等.視頻車(chē)輛監(jiān)控系統(tǒng)圖像濾波算法的研究[J].應(yīng)用光學(xué),2012,33(1):85-89.
[7]張全法,陳倩,王東亞.幀差分智能視頻監(jiān)控系統(tǒng)圖像亮度的校正[J].應(yīng)用光學(xué),2013,34(5):778-783.
[8]Russ J C.數(shù)字圖像處理[M].6版.余翔宇,等,譯.北京:電子工業(yè)出版社,2014:135-140.
[9]舒志龍,阮秋琦.一種二維均值濾波快速算法及其應(yīng)用[J].北方交通大學(xué)學(xué)報(bào),2001,25(2):22-24.