詳解如何在C/C++中測量一個函數(shù)或功能的運行時間
最常用的clock()
最常用的測量方法是使用clock()來記錄兩個 CPU 時間點clock_t,然后做差。這個方法的好處在于非常簡單易寫,如下(第一行是為說明需要導入哪個庫):
#include <time.h>
.....
clock_t begin = clock();
...需要被測量的代碼
clock_t end = clock();
int duration = (end - begin)/CLOCKS_PER_SEC;
需要注意 3 點:
CLOCKS_PER_SEC在 macOS 上是 1000000,也就是說(end - begin)的單位是微秒,所以要除以CLOCKS_PER_SEC。- 不同平臺的
clock_t類型是不一樣的,有些平臺是整數(shù)型,有些是使用浮點型。如果是浮點型的話,CLOCKS_PER_SEC或許可以不用寫,還是要看time.h的相關(guān)內(nèi)容。 - 這個方法中的“clock”一詞表示的是時鐘頻率,而不是時間。早期的計算機是固定頻率的(現(xiàn)在的一些計算器或者單片機其實也是固定頻率的),這個方法就是誕生于那個時間的。
這種方法最大的弊端就是它測量是 CPU 運行時間,準確的說是該進程使用 CPU 的時間。這就導致了對于非 CPU 密集型程序來說,這個結(jié)果可能不是那么精確。當然導致的最大的問題是并行計算程序得到的時間完全不對,而且不能簡單地使用核心數(shù)計算得到正確的時間。
因為這個方法是將多個 CPU 的運行時間加在一起了,串行計算的程序完全沒有問題,但是并行計算的話,CPU 使用率一般不會達到或接近核心數(shù)*100%,因為計算機上還有其他任務也需要 CPU。比如說如果串行計算的程序使用率一般在 99% 左右,獲取時間為 30 秒,但是對于 6 核的設(shè)備上運行的并行計算程序的話,CPU 使用率達到 570% 就很不錯了,獲取的時間可能為 27 秒,而實際上只用了 5 秒。
這是我在使用 ISPC 編寫并行計算程序的時候發(fā)現(xiàn)的,所以我想尋找到新的方案,于是我發(fā)現(xiàn)了下一個方法。
timespec
timespec是一個簡單的日歷時間或者時間流逝。通過使用日歷時間可以解決上一節(jié)中無法測量并行程序的實際運行時間的問題。但是“簡單”這點的表現(xiàn)為整數(shù)時間,也就是說最小的時間精度是秒,而不是上一種方法中的微秒,不過這對于復雜函數(shù)或程序的測試來說沒啥問題,畢竟 30 分鐘和 31 分鐘的性能差距不過 3.22%。
方法如下(第一行是為說明需要導入哪個庫):
#include <time.h>
time_t begin = time(NULL);
...需要被測量的代碼
time_t end = time(NULL);
int duration = (end - begin);
可以看到比上一種還要簡單。
但是對于一些小型的測試來說,這個方法又不太行,因為整數(shù)帶來的誤差太大了,比如說 0.6 秒是 1.8 秒性能的三倍,但是在整數(shù)上只為 2 倍甚至是 1 倍(為什么有這個“甚至”等會演示可以看到),所以還是需要一個更精確時間測量方法,這個方法不光要適應并行計算,還要有一定的精度。
clock_gettime()
clock_gettime()可以完美的符合要求,但是使用上有點復雜。
clock_gettime()是我從文檔的下面發(fā)現(xiàn)的。一開始我找到的是gettimeofday(),然后我去看了一下 IEEE 標準的文檔gettimeofday (opengroup.org),發(fā)現(xiàn)在“FUTURE DIRECTIONS(未來方向)”這一欄表示gettimeofday()可能未來會被廢棄;在“APPLICATION USAGE(應用使用)”這一欄表示應用應該使用clock_gettime()而不是gettimeofday。這必須得使用clock_gettime()了。

clock_gettime()的復雜之處在于太精確了。先來看看使用方法(第一行是為說明需要導入哪個庫):
#include <time.h>
struct timespec start;
clock_gettime(CLOCK_REALTIME, &start);
...需要被測量的代碼
struct timespec end;
clock_gettime(CLOCK_REALTIME, &end);
double duration = (double)(end.tv_nsec-start.tv_nsec)/((double) 1e9) + (double)(end.tv_sec-start.tv_sec);
clock_gettime()的參數(shù)CLOCK_REALTIME表示系統(tǒng)層面的實時時間;這個地方還可以用CLOCK_MONOTONIC,這個值是從系統(tǒng)啟動開始一直運行的,一直連續(xù)的不跳躍的(除非手動改了),這個要比CLOCK_REALTIME精度小一些,所以更快一些。
可以看到計算運行時間的代碼,也就是時間差的表達式長了很多,是因為clock_gettime()獲取的時間分為兩部分:秒和納秒(在某論壇上有人指出在曾經(jīng)的 Mac OS X 上這里是微秒,不確定,不過現(xiàn)在也是納秒了)。秒是int很簡單的,但是納秒用的是long int,這就涉及到轉(zhuǎn)換的問題了。所以就需要分別計算兩個部分,轉(zhuǎn)換合成。
實際演示(三種方法的對比)
這里展示一段并行計算程序在三種測量時間方法下的對比,各位可以看看差別(可以推測出測試設(shè)備是 6C6T 的 CPU 哦):

可以看到有時差別還是挺大的。
以上就是詳解如何在C/C++中測量一個函數(shù)或者功能的運行時間的詳細內(nèi)容,更多關(guān)于在C/C++中測量函數(shù)運行時間的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
VSCode 使用 Code Runner 插件無法編譯運行文件名帶空格的文件問題
這篇文章主要介紹了VSCode 使用 Code Runner 插件無法編譯運行文件名帶空格的文件問題,本文通過圖文實例相結(jié)合給大家介紹的非常詳細,需要的朋友可以參考下2021-07-07
Visual?Studio配置靜態(tài)庫詳細步驟和關(guān)鍵配置項
這篇文章主要介紹了Visual?Studio配置靜態(tài)庫的詳細步驟和關(guān)鍵配置項的相關(guān)資料,靜態(tài)庫是一系列預先編譯好的目標文件(.o文件或.obj文件)的集合,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2025-12-12
C語言實現(xiàn)學籍管理系統(tǒng)課程設(shè)計
這篇文章主要為大家詳細介紹了C語言實現(xiàn)學籍管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07

