【C語(yǔ)言進(jìn)階】使用gettimeofday為你的程序運(yùn)行時(shí)間做統(tǒng)計(jì)
有時(shí)候在編程調(diào)試的時(shí)候,會(huì)遇到個(gè)別性能問題需要調(diào)試,這時(shí)候我們需要比較精確地統(tǒng)計(jì)每段代碼的耗時(shí)情況,這種情況下,你是怎么做的呢?本文介紹一種方法來實(shí)現(xiàn)此功能。
1 需求背景
在項(xiàng)目編程中,如果遇到調(diào)試代碼性能的時(shí)候,慢慢需要加時(shí)戳來觀察,找出那些耗時(shí)的操作。這種情況大部分需要人工干涉,如果我們需要程序自動(dòng)幫忙完成耗時(shí)分析呢?我們可以怎么做呢?
本文就這個(gè)場(chǎng)景問題,提供一種解決方案,歡迎大家參考。
2 簡(jiǎn)要分析
2.1 算法分析
根據(jù)我們的常識(shí),我們知道,要想知道一個(gè)操作的耗時(shí),一般的做法就是在操作前取一個(gè)時(shí)間,然后操作后取一個(gè)時(shí)間;最后兩個(gè)時(shí)間相減,得到的就是這段操作的耗時(shí)。
根據(jù)這里方法論,我們可以比較快地實(shí)現(xiàn)邏輯代碼,但是我們應(yīng)該用哪個(gè)函數(shù)取當(dāng)前時(shí)間點(diǎn)呢?
2.2 gettimeofday簡(jiǎn)介
在Linux C語(yǔ)言編程中,我們很容易會(huì)想到gettimeofday這個(gè)函數(shù),下面我們將簡(jiǎn)單介紹一下這個(gè)函數(shù)。
參考Linux下的man說明,如下:
GETTIMEOFDAY(2) Linux Programmer's Manual GETTIMEOFDAY(2)
NAME
** gettimeofday, settimeofday - get / set time**
SYNOPSIS
** #include **
int gettimeofday(struct timeval *tv, struct timezone *tz); ? int settimeofday(const struct timeval *tv, const struct timezone *tz);
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
settimeofday(): Since glibc 2.19: _DEFAULT_SOURCE Glibc 2.19 and earlier: _BSD_SOURCE
DESCRIPTION
** The functions gettimeofday() and settimeofday() can get and set the time as well as a timezone.**
The tv argument is a struct timeval (as specified in ): ? struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; ? and gives the number of seconds and microseconds since the Epoch (see time(2)). ? The tz argument is a struct timezone: ? struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of DST correction */ }; ? If either tv or tz is NULL, the corresponding structure is not set or returned. (However, compilation warnings will result if tv is NULL.) ? The use of the timezone structure is obsolete; the tz argument should normally be specified as NULL. (See NOTES below.) ? Under Linux, there are some peculiar "warp clock" semantics associated with the settimeofday() system call if on the very first call (after booting) that has a non-NULL tz argument, the tv argument is NULL and the tz_minuteswest field is nonzero. (The tz_dsttime field should be zero for this case.) In such a case it is assumed that the CMOS clock is on local time, and that it has to be incremented by this amount to get UTC system time. No doubt it is a bad idea to use this feature.
RETURN VALUE
** gettimeofday() and settimeofday() return 0 for success, or -1 for failure (in which case errno is set appropriately).**
從這里我們可以知道,gettimeofday用于獲取當(dāng)前的時(shí)間是非常容易的,它會(huì)將時(shí)間結(jié)果存儲(chǔ)在這個(gè)結(jié)構(gòu)體中:
struct timeval {
** time_t tv_sec; /* seconds */
** suseconds_t tv_usec; /* microseconds / *
** };
struct timeval 結(jié)構(gòu)體中有 tv_sec 秒參數(shù) 和 tv_usec 微妙參數(shù),非常有利于我們做時(shí)間的加減計(jì)算。
下面我們就用這個(gè)函數(shù)來實(shí)現(xiàn)下本期的功能。
3 源碼實(shí)現(xiàn)
3.1 參考代碼
本例給出一個(gè)參考代碼如下:
#include
#include
#include
#include
?
static int get_cur_time_ms(void)
{
struct timeval tv;
?
gettimeofday(&tv, NULL); //使用gettimeofday獲取當(dāng)前系統(tǒng)時(shí)間
?
return (tv.tv_sec * 1000 + tv.tv_usec / 1000); //利用struct timeval結(jié)構(gòu)體將時(shí)間轉(zhuǎn)換為ms
}
?
3.2 代碼簡(jiǎn)介
代碼的功能邏輯,正如上面 的函數(shù)描述所說,正是利用gettimeofday返回的struct timeval結(jié)構(gòu)內(nèi)的成員變量,通過秒、毫秒、微妙的數(shù)量轉(zhuǎn)換,將當(dāng)前時(shí)間轉(zhuǎn)換成毫秒,然后以函數(shù)返回值的形式輸出。
值得注意的是,雖然gettimeofday在結(jié)構(gòu)體定義上 【號(hào)稱】有微妙級(jí)的精度,但是實(shí)際大部分的平臺(tái)是沒法達(dá)到這樣的精度的,所以我們?nèi)∑渲?,轉(zhuǎn)換成毫秒級(jí)的精度,這個(gè)已經(jīng)滿足我們絕大多數(shù)應(yīng)用場(chǎng)景了。
3.3 代碼測(cè)試
針對(duì)上面的功能代碼,寫了一段小代碼來測(cè)試下。
#include
#include
#include
#include
?
static int get_cur_time_ms(void)
{
struct timeval tv;
?
gettimeofday(&tv, NULL); //使用gettimeofday獲取當(dāng)前系統(tǒng)時(shí)間
?
return (tv.tv_sec * 1000 + tv.tv_usec / 1000); //利用struct timeval結(jié)構(gòu)體將時(shí)間轉(zhuǎn)換為ms
}
?
static void get_rand_bytes(unsigned char *data, int len)
{
int a;
int i;
?
srand((unsigned)time(NULL)); //種下隨機(jī)種子
for (i = 0; i < len; i++) {
data[i] = rand() % 255; //取隨機(jī)數(shù),并保證數(shù)在0-255之間
//printf("%02X ", data[i]);
}
}
?
int main(int argc, const char **argv)
{
int t1;
int t2;
unsigned char data[1024000];
?
t1 = get_cur_time_ms();
get_rand_bytes(data, sizeof(data));
t2 = get_cur_time_ms();
printf("random %d bytes, waste time: %dms
", sizeof(data), t2 - t1); //打印耗時(shí)
?
return 0;
}
?
以下測(cè)試代碼的邏輯還是很簡(jiǎn)單的,主要就是測(cè)試獲取 1024000個(gè)隨機(jī)數(shù)的耗時(shí)情況。
由于我本次測(cè)試使用的PC主機(jī),其性能還是不錯(cuò),所以測(cè)試出來的數(shù)據(jù),還是耗時(shí)比較小的;如果這段代碼放在嵌入式平臺(tái)去運(yùn)行的話,一個(gè)可能data數(shù)組的內(nèi)存會(huì)爆,第二個(gè)耗時(shí)可能會(huì)大大增加。
感興趣的朋友可以拿去一試。
注,本次測(cè)試的PC機(jī)情況如下:
4 小小總結(jié)
- 面對(duì)需求說明,盡快找到核心的功能要點(diǎn),找到算法邏輯是關(guān)鍵;
- 明白要做什么,再去選擇合適的函數(shù)來滿足功能邏輯,這是比較好的思路;
- 考慮到gettimeofday的實(shí)際精度問題,犧牲部分不起眼的精度,依然可以滿足大部分的需求。
5 更多分享
[架構(gòu)師李肯]
架構(gòu)師李肯 ( 全網(wǎng)同名 ),一個(gè)專注于嵌入式IoT領(lǐng)域的架構(gòu)師。有著近10年的嵌入式一線開發(fā)經(jīng)驗(yàn),深耕IoT領(lǐng)域多年,熟知IoT領(lǐng)域的業(yè)務(wù)發(fā)展,深度掌握IoT領(lǐng)域的相關(guān)技術(shù)棧,包括但不限于主流RTOS內(nèi)核的實(shí)現(xiàn)及其移植、硬件驅(qū)動(dòng)移植開發(fā)、網(wǎng)絡(luò)通訊協(xié)議開發(fā)、編譯構(gòu)建原理及其實(shí)現(xiàn)、底層匯編及編譯原理、編譯優(yōu)化及代碼重構(gòu)、主流IoT云平臺(tái)的對(duì)接、嵌入式IoT系統(tǒng)的架構(gòu)設(shè)計(jì)等等。擁有多項(xiàng)IoT領(lǐng)域的發(fā)明專利,熱衷于技術(shù)分享,有多年撰寫技術(shù)博客的經(jīng)驗(yàn)積累,連續(xù)多月獲得RT-Thread官方技術(shù)社區(qū)原創(chuàng)技術(shù)博文優(yōu)秀獎(jiǎng),榮獲[CSDN博客專家]、[CSDN物聯(lián)網(wǎng)領(lǐng)域優(yōu)質(zhì)創(chuàng)作者]、[2021年度CSDN&RT-Thread技術(shù)社區(qū)之星]、[2022年RT-Thread全球技術(shù)大會(huì)講師]、[RT-Thread官方嵌入式開源社區(qū)認(rèn)證專家]、[RT-Thread 2021年度論壇之星TOP4]、[華為云云享專家(嵌入式物聯(lián)網(wǎng)架構(gòu)設(shè)計(jì)師)]等榮譽(yù)。堅(jiān)信【知識(shí)改變命運(yùn),技術(shù)改變世界】!
審核編輯:湯梓紅
-
RT-Thread
+關(guān)注
關(guān)注
31文章
1293瀏覽量
40193
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論