探討效能測試中的計時問題

來源:互聯網
上載者:User

下載原始碼

摘要:本文結合作者在代碼效能測試工作中的經驗,介紹一組自己封裝的的計時函數。使用該組函數可以簡化測試工作,從而把更多的精力放在主要工作上,不需要過多地維護計時代碼,僅僅使用兩個宏就可以方便、精確地實現多個模組、多方式的時間效能測試,並且計時結果以一個文字檔獨立儲存,清晰直觀。

關鍵字:精確計時;效能測試;

在系統測試時,尤其在需要測試演算法或者某些模組的已耗用時間時,往往需要調用一些時間函數庫(如VC中的timeGetTime等可以擷取毫秒級的時間),在待測試的模組前後分別測試時間,然後,計算前後兩個時間的差值,就得到模組的已耗用時間, 1。



圖 1 一個典型的模組計時方法


但是,使用原始的計時函數直接進行時間測試在很多複雜情況下不方便, 1,當在一個模組中有多個子模組需要分別計時,所編寫的計時代碼甚至比原有的代碼還多,這增加了程式維護和閱讀的難度,容易出錯。作者結合自己在相關工作中的經驗,封裝了一組計時函數,共用給大家。該組函數有如下幾個優點:

  • 計時精確:封裝的是高精度的計時API函數QueryPerformanceCounter(),該函數根據硬體定時器的頻率,理論上可以得到微秒(us)級精度的計時結果;
  • 使用簡單:只用在待測試的模組前後加上兩個宏BM_START和BM_END,不需要對結果進行計算,也不需要考慮對各個模組測試結果資料的維護,這些操作已經被封裝。
  • 結果輸出獨立:在系統運行結果時,只需要調用一個函數就可以把計時結果儲存在一個文字檔裡, 5和圖 8所示。

1. 高精度計時函數
在Windows系統下,程式員通常可以使用多種方式來進行時間控制:如使用前文提到的timeGetTime()函數,或者使用GetTickCount()函數,又或實現WM_TIMER訊息的映射等等。但是這些方法得到的時間精度都有一定的局限性,為了增加下文將到介紹的計時函數庫的適用性,本文採用高精度的時控API函數QueryPerformanceCounter()。

計時之前,調用QueryPerformanceFrequency()函數獲得機器內部定時器的時鐘頻率,然後在需要計時的模組前後分別調用QueryPerformanceCounter()函數,利用兩次獲得的計數之差獲得時鐘頻率,計算出模組的已耗用時間。代碼 2:

圖 2 精確計時程式碼片段

2. 封裝計時函數
2.1. 資料結構
為了維護計時結果,我們定義如下幾個資料:

#define BENCHMARK_MAX_COUNT 20double gStarts[BENCHMARK_MAX_COUNT];double gEnds[BENCHMARK_MAX_COUNT];double gCounters[BENCHMARK_MAX_COUNT];double dfFreq = 1;


其中,BENCHMARK_MAX_COUNT定義了需要計時的模組總數,20表示最多可以定時20個模組,該值可以根據具體應用而定。gStarts和gEnds分別用於儲存開始計時和終止計時的計數器的值,gCounters用來儲存計時結果。全域變數dfFreq用來儲存上文介紹的時鐘頻率, 2所示。

2.2. 初始化InitBenchmark()
初始化函數InitBenchmark()包括兩部分內容:

  • 對數組gStarts, gEnds, gCounter清零;
  • 獲得機器內部定時器時鐘頻率。

InitBenchmark()代碼如下所示:

void InitBenchmark(){         ResetBenchmarkCounters();         GetClockFrequent();}

該函數一般在程式運行最初調用。

2.3. 開始計時BMTimerStart()
開始計時函數BMTimerStart()放在計時模組的開始,函數定義如下:

void BMTimerStart(int iModel){         LARGE_INTEGER litmp;         QueryPerformanceCounter(&litmp);         gStarts[iModel] = litmp.QuadPart;}

其中參數iModel表示當前計時的模組序號,0<=iModel<=BENCHMARK_MAX_COUNT;為了簡化調用代碼,我們給出一個宏定義如下:

#define BM_START(t)                  BMTimerStart(t);

2.4. 終止計時BMTimerEnd()
終止計時函數BMTimerEnd()放在計時模組的結束,函數定義如下:

void BMTimerEnd(int iModel){                LARGE_INTEGER litmp;         QueryPerformanceCounter(&litmp);         gEnds[iModel] = litmp.QuadPart;         gCounters[iModel] += (((gEnds[iModel] - gStarts[iModel]) / dfFreq) * 1000000);}

參數iModel同BMTimerStart()。本函數首先擷取當前的時鐘數,然後除以dfFreq得到已耗用時間。對於最後一條語句:

gCounters[iModel] += (((gEnds[iModel] - gStarts[iModel]) / dfFreq) * 1000000);

要注意兩點:

  • 用“+=”而不是“=”,這個看似簡單的代替,可以實現對同一個模組的重複計時,後文3.3節列舉的情況;
  • 乘以1000000,表示計時單位為微秒(us)。

類似BMTimerStart(),同樣為BMTimerEnd()定義一個宏:

#define BM_END(t)             BMTimerEnd(t);

2.5. 結果輸出WriteData()
以一個文字檔(見圖 5和圖 8)把全域變數gCounters中的所有值輸出,該函數一般在程式結束處調用, 4中最後一行代碼所示。由於篇幅限制,具體實現代碼請參考來源程式。

3. 計時測試執行個體
3.1. 多個模組計時
圖 3展示了嵌套計時以及對一個函數中多個模組進行計時的代碼,圖中可以看到,利用輸入參數我們對計時模組進行統一編號,測試代碼相對圖 1更清晰、直觀。



圖 3 用我們的函數實現嵌套計時

3.2. 迴圈內部計時
圖 4中的代碼展示了我們對迴圈體內每次執行運算的計時,只需簡單地給出參數 就可以得到從1到20的階乘的每次計算計算時間,計時結果輸出為檔案“D:\log.txt”, 5。



圖 4 對迴圈中每次運算的計時



圖 5 計時結果1

3.3. 迴圈累加計時
從圖 5可以看到,由於現代電腦處理速度越來越快,一些簡單運算的模組,微秒的計時單位幾乎都不夠精確,因此,一種常用的測試方法就是對同一模組進行N (N 取1000,10000等)次重複執行。使用本文介紹的計時函數,我們可以採用兩種方式對這種情況進行測試,代碼分別 6和圖 7,請注意二者的區別,並請讀者分析為何圖 7中的方法也是可行的。 N次運算計時結果 8。


圖 6 累加計時1

圖 7 累加計時2



圖 8 計時結果2

4. 結束語

本文實現了一組計時函數的封裝,並給出幾種特殊情況下的測試執行個體,實驗表明該組函數可以滿足各種複雜情況下的計時,能夠很方便地應用的實際的測試工作中。當然,還可以進一步封裝成一個計時類,留給讀者們自己去做。

原為轉自

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.