0. 寫在最前面
本文持續更新地址:https://haoqchen.site/2019/12/17/linux-time-summary/
最近寫程序涉及到時間相關的,包括當前時間呀,進程運行的時間差呀,線程某段程序的時間消耗呀等等。然後查了比較多Linux下的時間函數。發現每個函數之間都有或多或少的區別,應用場景很不一樣。在此做個總結和記錄。如果沒時間細看可以直接跳到第3章的總結。
如無特殊說明,我的系統是Ubuntu1604(64bit)
對ROS的時間有興趣的可以看看我的另一篇文章https://haoqchen.site/2018/11/08/ROS-time/
如果覺得寫得還不錯,可以找我其他文章來看看哦~~~可以的話幫我github點個讚唄。
你的Star是作者堅持下去的最大動力哦~~~
1. 系統類函數
這裏所說的系統類函數主要是C標準庫中的函數。
1.1 gettimeofday
在終端下運行man gettimeofday
可以看到其官方說明,我摘錄一些重點:
項目 | 說明 | 備註 |
---|---|---|
頭文件 | #include <sys/time.h> | |
原型 | int gettimeofday(struct timeval *tv, struct timezone *tz); | |
功能 | 獲取從Epoch (1970年1月1日00:00:00 UTC,到2038年會掛那個)到當前所經過的時間(不考慮閏秒)以及當前時區,分辨率達us |
|
return | 成功返回0,失敗返回-1,可通過errno查看錯誤碼 |
其中struct timeval
:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
其中time_t
和suseconds_t
是跟系統有關的類型,我在自己系統下看到的是long int
用法:
struct timeval t_start, t_end;
gettimeofday(&t_start, NULL);
do_something();
gettimeofday(&t_end, NULL);
long int time_cost = (t_start.tv_sec - t_end.tv_sec) * 1000 + (t_start.tv_usec - t_end.tv_usec)
printf("Cost time: %ld ms\n", time_cost);
1.2 times
項目 | 說明 | 備註 |
---|---|---|
頭文件 | #include <sys/times.h> | |
原型 | clock_t times(struct tms *buf); | |
功能 | 返回當前進程的相關時間,包括用戶時間,系統時間,子進程用戶時間,子進程系統時間 | |
return | 失敗時返回-1,成功時返回過去某一時間點到現在經過的CPU計數,每秒的脈衝數用sysconf(_SC_CLK_TCK) 獲取 |
這個值可能溢出 |
其中tms
,單位都是CPU計數:
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
1.3 clock_gettime
項目 | 說明 | 備註 |
---|---|---|
頭文件 | #include <time.h> | |
原型 | int clock_gettime(clockid_t clk_id, struct timespec *tp); | |
功能 | 獲取系統某一時鐘從Epoch 到當前的時間,可精確到納秒 |
|
return | 成功返回0,失敗返回-1 |
其中timespec
:
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
可用的時鐘:
CLOCK_REALTIME
:就是所說的wall-clock
,會受非連續跳躍影響,比如人爲修改了時鐘。或者增量的調整,比如使用adjtime
函數CLOCK_REALTIME_COARSE
:精簡版的CLOCK_REALTIME
,很快,但精度受損。CLOCK_MONOTONIC
:單調的時鐘?從某個不確定的時刻開始跳動,不受非連續跳躍的影響,但是受增量的調整影響。CLOCK_MONOTONIC_COARSE
:精簡版的CLOCK_MONOTONIC
,很快,但精度受損。CLOCK_MONOTONIC_RAW
:與CLOCK_MONOTONIC
類似,但輸出的是原始的時鐘,不受其他調整影響CLOCK_BOOTTIME
:與CLOCK_MONOTONIC
完全一致,但把系統暫停的時間也算在內CLOCK_PROCESS_CPUTIME_ID
:統計進程所有線程消耗的時間CLOCK_THREAD_CPUTIME_ID
:線程消耗的時間,這個ID是針對當前線程的,如果想獲得其他線程的ID,需要調用pthread_getcpuclockid
用法:
#include <time.h>
#include <string>
#include <sstream>
auto time2String = [](struct timespec t_start, struct timespec t_end) -> const std::string {
std::string result;
std::stringstream ss;
long double temp = 0.0;
temp += (t_end.tv_sec - t_start.tv_sec);
temp += static_cast<long double>((t_end.tv_nsec - t_start.tv_nsec) / 1000u) / static_cast<long double>(1000000.0);
ss.precision(6);
ss.setf(std::ios::fixed);
ss << temp ;
ss >> result;
return result;
};
struct timespec time_start;
struct timespec time0;
time0.tv_sec = 0;
time0.tv_nsec = 0;
if (0 == clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_start)){
std::cout << "start time: " << time2String(time0, time_start) << "--------------" << std::endl;
}
struct timespec time_1;
if (0 == clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_1)){
std::cout << "1st time: " << time2String(time_start, time_1) << std::endl;
}
1.4 clock
同樣可從man中看到相關說明
項目 | 說明 | 備註 |
---|---|---|
頭文件 | #include <time.h> | |
原型 | clock_t clock(void); | |
功能 | 返回程序所用的處理器時間近似,也就是目前爲止所用的CPU時間,是否包括sleep的時間與系統有關 | 在32位系統由於位數關係,約每72分鐘循環一次 |
return | 返回CPU的時鐘計數,錯誤返回-1。clock_t 在我的系統下是long int ,要獲得時間,需要除以CLOCKS_PER_SEC |
注意:
- Linux中,返回的時間不包括wait子線程的時間,其他系統不確定。可通過
times
函數來獲得 - 在glibc 2.17及之前的版本,clock是基於
times
來實現的,在後續版本,爲了提高精度,其基於clock_gettime
實現。 - 在C標準中,程序開始時clock的返回值可爲任意值,每個系統的實現會有不同,所以最好的方式是程序開始時獲取一個初始值,後面的時間減去這個初始值。
CLOCKS_PER_SEC
在所有XSI-conformant
系統中被定義爲1000000,也即時間分辨率爲1us
注:個人感覺這個函數沒啥用,精度不及其他,然後又諸多限制
2. C++標準類函數
2.1 time
項目 | 說明 | 備註 |
---|---|---|
頭文件 | #include | |
原型 | std::time_t time( std::time_t *time ); | |
功能 | 返回日曆時間,即從Epoch 到當前所經過的秒數 |
|
return | 如上,失敗時返回-1 |
一般情況下,我們需要的是時間描述,而不是一個乾巴巴的秒數,所以會調用struct tm * localtime (const time_t * timer);
函數將其轉換成一個時間描述結構,其包括:
int tm_sec
int tm_min
int tm_hour
int tm_mday
int tm_mon
int tm_year
int tm_wday
int tm_yday
int tm_isdst
如上所述,時間精確到秒。
如果你想進一步轉換成字符串描述,可以調用char* asctime( const struct tm* time_ptr );
進行轉換。
參考:
- http://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/cpp/chrono/c/time.html
- http://www.cplusplus.com/reference/ctime/localtime/
- https://zh.cppreference.com/w/c/chrono/asctime
2.2 std::chrono時間庫
詳見chrono的CPPReference說明,這個庫主要提供三個時鐘的獲取和處理,這三個時鐘被封裝成類,並且都是通過返回當前的time_point
時間節點來獲得時間。在chrono庫中,所有的時間節點都是相對於Epoch
的
時鐘類 | 特點 |
---|---|
steady_clock | 1. 設計用於計算時間間隔 2. 計數間隔是穩定的,間隔1ns(官方說明的代碼受double精度限制是us的) 3. 一般是系統啓動的時間,且保證後面的時間永遠不比前面得到的時間小 |
system_clock | 1. 設計用於表示真實時間,即日曆時間,刻度爲1個tick,_XTIME_NSECS_PER_TICK納秒 2. 時間點可爲負數 3. 系統中所有進程用該時鐘,時間節點都是一樣的 4. 可與 time_t 相互轉換 |
high_resolution_clock | 1. 該時鐘是系統中頻率最高的 2. 該時鐘有可能與上述兩個時鐘是一樣的 |
參考:
3. 使用建議
- 自由度最高的是clock_gettime
- 由於系統存在線程調度的問題,所以獲取日曆時鐘計算時間差都是有可能包含其他無關線程的執行時間的(一般約10ms)。要想只獲取當前線程的時間差,貌似只能用clock_gettime
- 如果只關心自身進程,可使用clock_gettime、clock、times,其中
times
還可以方便地統計哪些是系統消耗的,哪些是自身消耗的。 - 如果是想方便地輸出字符串信息,可使用C++的time
- 其實目前的晶振頻率都挺高的,無論用哪個時鐘,精度都能得到保證,更多地應該考慮系統調度的問題。
參考
- https://www.cnblogs.com/zhongpan/p/7490657.html
- https://stackoverflow.com/questions/38252022/does-standard-c11-guarantee-that-high-resolution-clock-measure-real-time-non
喜歡我的文章的話Star一下唄Star
版權聲明:本文爲白夜行的狼原創文章,未經允許不得以任何形式轉載