如何精確測量程序運(yùn)行時(shí)間_第1頁
如何精確測量程序運(yùn)行時(shí)間_第2頁
如何精確測量程序運(yùn)行時(shí)間_第3頁
如何精確測量程序運(yùn)行時(shí)間_第4頁
如何精確測量程序運(yùn)行時(shí)間_第5頁
已閱讀5頁,還剩3頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、如何精確測量程序運(yùn)行時(shí)間如何精確測量程序運(yùn)行時(shí)間 2011-04-24 20 : 18如何精確測量程序運(yùn)行時(shí)間、,- 、-刖言對于一個(gè)嵌入式程序員來說,我的程序到底運(yùn)行多快,是我們最為關(guān)心 的問題,因?yàn)樗俣?,?shí)時(shí)性,永遠(yuǎn)是嵌入式設(shè)備性能優(yōu)化的基本立足點(diǎn)之一。 可惜的是,我們平時(shí)常用的測試運(yùn)行時(shí)間的方法,并不是那么精確的。換句話 說,想精確獲取程序運(yùn)行時(shí)間,不是那么容易的。也許你會想,程序不就是一 條條指令么,每一條指令序列都有固定執(zhí)行時(shí)間,為什么不好算?真實(shí)情況下,我們的計(jì)算機(jī)并不是只運(yùn)行一個(gè)程序的,進(jìn)程的切換,各種中斷,共享的多用 戶,網(wǎng)絡(luò)流量,高速緩存的訪問,轉(zhuǎn)移預(yù)測等,都會對計(jì)時(shí)產(chǎn)生影響

2、??上У氖?,在性能測量領(lǐng)域,我們有 gprof,有in tel的vtu ne,卻缺少相 應(yīng)的,廣泛流傳的參考文獻(xiàn)。如果你希望能建立起自己的工具,或者對具體的 測量方式感興趣,那么本文也許會對你有幫助。我想,應(yīng)該有很多人希望知道 計(jì)時(shí)機(jī)制的原理,因?yàn)獒槍Σ煌南到y(tǒng),環(huán)境,會有不同的解決方案。本文主 要針對Linux和X86體系環(huán)境,主要思想來源于Computer System AProgrammers Perspective,夾雜了一些自己的理解,并試圖給出我自己寫 的一個(gè)通用測量工具,支持用戶自配置。本文有時(shí)的對象是程序有時(shí)描述對象 是進(jìn)程,這個(gè)請自行理解,因?yàn)橐粋€(gè)程序就是在一個(gè)進(jìn)程里面執(zhí)行的

3、。進(jìn)程調(diào)度和模式切換在介紹具體方法之前,先簡單說幾句。對于進(jìn)程調(diào)度來講,花費(fèi)的時(shí)間分為三部分,第一是計(jì)時(shí)器中斷處理的時(shí) 間,也就是當(dāng)且僅當(dāng)這個(gè)時(shí)間間隔的時(shí)候,操作系統(tǒng)會選擇,是繼續(xù)當(dāng)前進(jìn)程 的執(zhí)行,還是切換到另外一個(gè)進(jìn)程中去。第二是進(jìn)程切換時(shí)間,當(dāng)系統(tǒng)要從進(jìn) 程A切換到進(jìn)程B時(shí),它必須先進(jìn)入內(nèi)核模式將進(jìn)程 A的狀態(tài)保存,然后恢復(fù) 進(jìn)程B的狀態(tài)。因此,這個(gè)切換過程是有內(nèi)核活動來消耗時(shí)間的。第三就是進(jìn) 程的具體執(zhí)行時(shí)間了,這個(gè)時(shí)間也包括內(nèi)核模式和用戶模式兩部分,模式之間 的切換也是需要消耗時(shí)間,不過都算在進(jìn)程執(zhí)行時(shí)間中了。其實(shí)模式切換非常費(fèi)時(shí),這也是很多程序中都要采用緩沖區(qū)的原因,例如, 如果每讀

4、一小段文件什么的就要調(diào)用一次 read之類的內(nèi)核函數(shù),那太受影響了。 所以,為了盡量減少系統(tǒng)調(diào)用,或者說,減少模式切換的次數(shù),我們向程序(特別是10程序)中引入緩沖區(qū)概念,來緩解這個(gè)問題。一般來說呢,向處理器發(fā)送中斷信號的計(jì)時(shí)器間隔通常是1-10ms,太短,切換太多,性能可能會變差,太長呢,如果在任務(wù)間切換頻繁,又無法提供在 同時(shí)執(zhí)行多任務(wù)的假象。這個(gè)時(shí)間段,也決定了一些我們下面要分析的不同方 法衡量時(shí)間的差異。方法一:間隔計(jì)數(shù)我們都知道,Linux下有一個(gè)命令是專門提供一個(gè)進(jìn)程的運(yùn)行時(shí)間的,也 就是time。time可以測量特定進(jìn)程執(zhí)行時(shí)所需消耗的時(shí)間及系統(tǒng)資源等,這個(gè) 時(shí)間還可以分內(nèi)核時(shí)間

5、和用戶態(tài)時(shí)間兩部分呈現(xiàn)給你。它是怎么做到的呢?其實(shí)很簡單,操作系統(tǒng)本身就是用計(jì)時(shí)器來記錄每個(gè)進(jìn)程使用的累計(jì)時(shí)間,原理很 簡單,計(jì)時(shí)器中斷發(fā)生時(shí),操作系統(tǒng)會在當(dāng)前進(jìn)程列表中尋找哪個(gè)進(jìn)程是活動 的,一旦發(fā)現(xiàn),喲,進(jìn)程 A跑得正歡,立馬就給進(jìn)程 A的計(jì)數(shù)值增加計(jì)時(shí)器的 時(shí)間間隔(這也是引起較大誤差的原因,想想)。當(dāng)然不是統(tǒng)一增加的,還要確 定這個(gè)進(jìn)程是在用戶空間活動還是在內(nèi)核空間活動,如果是用戶模式,就增加 用戶時(shí)間,如果是內(nèi)核模式,就增加系統(tǒng)時(shí)間。原理很簡單吧?但是相信一點(diǎn),越簡單的東西,是不會越精確的,人品守恒, 能量守恒,難度也當(dāng)然會守恒了啊。下面就簡單分析一下,為啥這玩意精度不 高吧。舉個(gè)例

6、子,如果我們有一個(gè)系統(tǒng),計(jì)時(shí)器間隔為 10ms系統(tǒng)里面跑了一 個(gè)進(jìn)程,然后我們用這種方法分析時(shí)間,測出70ms想一想,實(shí)際會有幾種結(jié)果?具體點(diǎn),我們用這種方法對進(jìn)程計(jì)時(shí),在某個(gè)計(jì)時(shí)器中斷時(shí),系統(tǒng)發(fā)現(xiàn),咦, 有一個(gè)進(jìn)程開始跑了,好,給進(jìn)程的計(jì)數(shù)值加上10ms但是實(shí)際上呢,這個(gè)進(jìn)程可能是一開始就跑起來了,也肯能是在中斷的前1ms才開始跑的。不管是什么原因,總之中斷時(shí)候它在跑,所以就得加 10ms當(dāng)中斷發(fā)生時(shí)發(fā)現(xiàn)進(jìn)程切換 了,同理,可能是上一個(gè)中斷之后 1ms進(jìn)程就切換了,也可能人家剛剛才切換。所以呢,如果一個(gè)進(jìn)程的運(yùn)行時(shí)間很短,短到和系統(tǒng)的計(jì)時(shí)器間隔一個(gè)數(shù) 量級,用這種方法測出來的結(jié)果必然是不夠準(zhǔn)

7、確的,頭尾都有誤差。不過如果 程序的時(shí)間足夠長,這種誤差有時(shí)能夠相互彌補(bǔ),一些被高估一些被低估,平 均下來剛好,呵呵。從理論上,我們很難分析這個(gè)誤差的值,所以一般只有程 序到達(dá)秒的數(shù)量級時(shí),用這種方式測試程序時(shí)間才有意義。說了半天,難道這方法沒優(yōu)點(diǎn)了 ?不,這個(gè)世界沒有純善,也沒有純惡。這 方法最大的優(yōu)點(diǎn)是,它的準(zhǔn)確性不是非常依賴于系統(tǒng)負(fù)載。那什么方法依賴于 系統(tǒng)負(fù)載呢?接下來我們會講到:)理論陳述結(jié)束,我想應(yīng)該開始關(guān)注實(shí)現(xiàn)方法了吧。其實(shí)超級簡單,兩種方法:直接調(diào)用time命令(一堆雞蛋)使用tms結(jié)構(gòu)體和times函數(shù)說說正經(jīng)點(diǎn)的 第二個(gè)方法吧。在Linux中,提供了一個(gè)times函數(shù),原型

8、是clock_t times(struct tms*buf)這個(gè)tms的結(jié)構(gòu)體為struct tmsclock_t tms_utime ; /user time clock_t tms_stime ; /system time clock_t tms_cutime ; /user time of reaped children clock_t tms_cstime /system time of reaped childre n怎么使用就不用這里教了吧?不過要說明一下的是,這里的 cutime和 cstime,都是對已經(jīng)終止并回收的時(shí)間的累計(jì),也就是說,times不能監(jiān)視任何正在進(jìn)行中的子進(jìn)程

9、所使用的時(shí)間。方法二:周期計(jì)數(shù)岡財(cái)談了半天間隔計(jì)數(shù)的不足之處,哪有不足,那就有彌補(bǔ)的方法,特別 實(shí)在萬能的Linux中:)為了給計(jì)時(shí)測量提供更高的準(zhǔn)確度,很多處理器還包含 一個(gè)運(yùn)行在時(shí)鐘周期級別的計(jì)時(shí)器,它是一個(gè)特殊的寄存器,每個(gè)時(shí)鐘周期它 都會自動加1。這個(gè)周期計(jì)數(shù)器呢,是一個(gè) 64位無符號數(shù),直觀理解,就是如 果你的處理器是1GHz的,那么需要570年,它才會從2的64次方繞回到0, 所以你大可不必考慮萬一溢出怎么辦此類問題??吹竭@里,也許你會想,哇塞,很好很強(qiáng)大嘛,時(shí)鐘周期,這都精確到小 數(shù)點(diǎn)后面多少位來著了 ?這下無論是多快的用時(shí)多短的程序,我們也都能進(jìn)行時(shí) 間測量了。Ohyeah等等

10、,剛才我們說過什么來著?守恒定律??!功能強(qiáng)大的東 西,其他方面必有限制嘛。看到上面的介紹,聰明的你一定能猜出來這種方法 的限制是什么了,那就是,hardware dependent。首先,并不是每種處理器都 有這樣的寄存器的,其次,即使大多數(shù)都有,實(shí)現(xiàn)機(jī)制也不一樣,因此,我們 無法用統(tǒng)一的,與平臺無關(guān)的接口來使用它們。怎么辦?這下,就要祭出上古傳說中的神器:匯編了。當(dāng)然,我們在這里實(shí)際用的是C語言的嵌入?yún)R編:void counter(un sig ned*hi,u nsig ned*lo)asm(rdtsc ; movl%edx,%; movl%eax,%1:=r(*hi),=r(*lo):%

11、edx,%eax);第一行的指令負(fù)責(zé)讀取周期計(jì)數(shù)器,后面的指令表示將其轉(zhuǎn)移到指定地點(diǎn) 或寄存器。這樣,我們將這段代碼封裝到函數(shù)中,就可以在需要測量的代碼前后均加上這個(gè)函數(shù)即可。最后得到的hi和Io值都是兩個(gè),除了相減得到間隔值外,還要進(jìn)行一些處理,在此先按下不表。不得不提出的是,周期計(jì)數(shù)方式還有一個(gè)問題,就是我們得到了兩次調(diào)用 counter之間總的周期數(shù),但我們不知道是哪個(gè)進(jìn)程使用了這些周期,或者說 處理器是在內(nèi)核還是在用戶模式中。還記得剛才我們講間隔計(jì)數(shù)方式么?這玩意的好處就是它是操作系統(tǒng)控制給進(jìn)程計(jì)時(shí)的,我們可以知道具體哪個(gè)進(jìn)程,哪 個(gè)模式。但是周期計(jì)數(shù)只測量經(jīng)過的時(shí)間,他不管你是哪個(gè)進(jìn)

12、程使用的。所以, 用周期計(jì)數(shù)的話,我們必須很小心。舉個(gè)例子double time()start_c oun ter();P();get_c oun ter();這樣一段程序,如果機(jī)器的負(fù)載很重,會導(dǎo)致P運(yùn)行時(shí)間很長,而其實(shí)P函數(shù)本身是不需要運(yùn)行這么長時(shí)間的,而是上下文切換等過程將它的時(shí)間拖長 了。而且,轉(zhuǎn)移預(yù)測(想一想,如果轉(zhuǎn)移方向和目的預(yù)測錯誤)和高速緩存的命 中率,對這個(gè)計(jì)數(shù)值也會有影響。通常情況下,為了減少高速緩存不命中給我 們程序執(zhí)行時(shí)間帶來的影響,可以執(zhí)行這樣的代碼:double time_warm(void)P();start_c oun ter()?p();get_c oun t

13、er();原因不用我再解釋了吧?它讓指令高速緩存和數(shù)據(jù)高速緩存都得到了warm-up。好,接下來又有問題。如果我們的應(yīng)用,是屬于那種每次執(zhí)行都希望訪問 新的數(shù)據(jù)的那種呢?在這種情況下,我們希望讓指令高速緩存warm-up,而數(shù)據(jù)高速緩存不能warm-up,很明顯,time_warm函數(shù)低估我們的運(yùn)行時(shí)間了。讓我 們進(jìn)行進(jìn)一步修改:double time_cold(void)P();clear_cache();start_c oun ter();P();get_c oun ter();注意,我們加入了一個(gè)清除數(shù)據(jù)緩存的函數(shù)。這個(gè)函數(shù)的具體實(shí)現(xiàn)很簡單, 依情況而定,比如舉個(gè)例子volatile i

14、nt tmpstatic int dummyN ; N是你需要清理緩存的字節(jié)數(shù)void clear_cache(void)inti,sum=0 ;for(i=1 ; i N ; i+)dummyi=2 ;for(i=1 ; i N ; i+)sum+=dummyi;tmp=sum具體原理很簡單,我們在定義一個(gè)數(shù)組并在其上執(zhí)行一個(gè)計(jì)算,計(jì)算過程中的數(shù)據(jù)會覆蓋高速數(shù)據(jù)緩存中原有的數(shù)據(jù)。每一次的 store和load都會讓高 速數(shù)據(jù)緩存cache這個(gè)數(shù)組,而定義為volatile 的tmp則保證這段代碼不會被 優(yōu)化。這樣做,是不是就萬無一失了呢?不是的,因?yàn)榇蠖鄶?shù)處理器,L2高速緩 存是不分指令和數(shù)

15、據(jù)的,這樣 clear_cache會讓所有P的指令也被清除,只不 過:L1緩存中的指令還會保留而已。其實(shí)上面提到的諸多原因,都是我們不能控制的,我們無法控制讓高速緩 存去加載什么,不去加載什么,加載時(shí)去掉什么,保留什么。而且,這些誤差 通常都是會過高估計(jì)真實(shí)的運(yùn)行時(shí)間。那么具體使用時(shí),有沒有什么辦法來改 善這種情況呢?有,就是 The K-Best Measurement Scheme。這玩意其實(shí)很麻煩, 所以我在具體實(shí)踐中都不用它,附上一個(gè)文檔,有興趣的朋友可以下載下來看 一下。我不喜歡間隔計(jì)數(shù)的小適用范圍,也不喜歡周期計(jì)數(shù)的麻煩性,相信讀到 這里的99%勺讀者也和我一種感受吧。0K最后我們

16、要介紹的,就是一個(gè)可移 植性更好,相對較準(zhǔn)確的方法。方法三:gettimeofday 函數(shù)計(jì)時(shí)gettimeofday是一個(gè)庫函數(shù),包含在time.h中。它的功能是查詢系統(tǒng)時(shí) 鐘,以確定當(dāng)前的日期和時(shí)間。它很類似于剛才所介紹的周期計(jì)時(shí),除了測量 時(shí)間是以秒為單位,而不是時(shí)鐘周期為單位的。原型如下:struct timevallong tv_sec ;long tv_usec ;int gettimeofday(struct timeval*tv,NULL)這個(gè)機(jī)制呢,具體的實(shí)現(xiàn)方式在不同系統(tǒng)上是不一樣的,而且雖然披著一 個(gè)usec(us)的老虎皮,其實(shí)沒這么精確。具體的精確程度,是和系統(tǒng)相關(guān)的, 比如在Linux下,是用周期計(jì)數(shù)來實(shí)現(xiàn)這個(gè)函數(shù)的,所以和周期計(jì)數(shù)的精確度 差不多,但是在 Windows NT下,使用間隔

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論