本篇博客主要介绍统计程序耗时的一些方法,以及与其相关的一些函数与概念的介绍与说明。

统计程序耗时

time 类别

UTC时间: 世界时钟,Coordinated Universal time
日历时间: Calendar time, 相对时间。“从一个标准时间点到此时的时间经过的秒数”来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是“相对时间”,但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。
epoch: 时间点,整数,此时的时间与标准时间点相差的秒数。
clock tick: 时钟滴答数,与cpu相关,一个 clock tick不是一个CPU时钟周期,而是C/C++的基本计时单位。

系统CPU时间: 程序在系统核心态中执行的时间
用户CPU时间: 程序在用户态中执行的时间
实际运行时间: 命令从执行到终止的时间

计时函数

clock(): 获取cpu时间

定义介绍: 计算CPU耗时(系统CPU时间 + 用户CPU时间)

1
2
3
4
5
6
7
8
9
10
11
clock_t clock(void);

// 返回类型为 clock_t, 在time.h中,可以找到其定义为 long 类型
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif

// 与之相关的还有一个常量 CLOCKS_PER_SEC, 在不同平台,定义不同
// VC中定义1000个滴答为一秒,在 POSIX 中每过 1000000 个滴答为一秒
#define CLOCKS_PER_SEC ((clock_t)1000)

使用示例:

1
2
3
4
clock_t c_start = clock();
do_something();
clock_t c_end = clock();
std::cout <<"CPU time: " << " " << (double)(c_end - c_start) / (double) CLOCKS_PER_SEC << " s\n";

问题说明:

  • 如果超过一个小时,将要导致溢出.
  • 函数clock没有考虑CPU被子进程使用的情况.
  • 也不能区分用户空间和内核空间.

times():获取具体的cpu时间

定义介绍: 返回的是过去一段时间内时钟滴答的次数

1
2
3
4
5
6
7
8
9
10
#include <time.h>
// 原型如下:
clock_t times(struct tms *buf);
// tms结构体如下:
strace tms{
clock_t tms_utime; //进程执行用户代码的时间
clock_t tms_stime; //进程执行内核代码的时间
clock_t tms_cutime; //子进程执行用户代码的时间
clock_t tms_cstime; //子进程执行内核代码的时间
}

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <sys/types.h>
#include <sys/times.h>

struct tms tmsstart, tmsend;
clock_t start, end;
start=times(&tmsstart); //初始时间
do_something();
end=times(&tmsend); //结束时间

//总时间
static long clktck = sysconf(_SC_CLK_TCK);
printf("real:%7.2f\n", (end -start)/(double)clktck);
//用户态 cpu 时间
printf("user-cpu:%7.2f\n", (tmsend->tms_utime - tmsstart->tms_utime)/(double)clktck);
//核心态 cpu 时间
printf("system-cpu:%7.2f\n", (tmsend->tms_stime - tmsstart->tms_stime)/(double)clktck);
//子进程 用户态 cpu 时间
printf("child-user-cpu:%7.2f\n", (tmsend->tms_cutime - tmsstart->tms_cutime)/(double)clktck);
//子进程 核心态 cpu 时间
printf("child-system-cpu:%7.2f\n", (tmsend->tms_cstime - tmsstart->tms_cstime)/(double)clktck);

clock_gettime():获取线程时间

定义: 基于 Linux C的时间函数,也可以用于计算精度和纳秒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <time.h>

// 原型如下
int clock_gettime(clockid_t clk_id, struct timespec *tp);

// timespec 结构体
struct timespec{
time_v tv_sec; //秒
time_v tv_nsec; //纳秒
};

//clockid_t 检索或设置的 clk_id 指定的时钟时间
//CLOCK_REALTIME: 系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时, 中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
//CLOCK_MONOTONIC: 从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
//CLOCK_PROCESS_CPUTIME_ID: 本进程到当前代码系统CPU花费的时间
//CLOCK_THREAD_CPUTIME_ID: 本线程到当前代码系统CPU花费的时间

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void constructFBString(int size){
struct timespec timeStart, timeEnd;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timeStart); //获取当前的线程时间
for(int i=0; i<10000; i++)
{
fbstring fbstr1(size, 'a');
fbstring fbstr2(size, 'b');
fbstr1 += fbstr2;
fbstr1.find('b');
}
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &timeEnd); //获取当前的线程时间
double res = (timeEnd.tv_sec - timeStart.tv_sec)* 1000 + (timeEnd.tv_nsec-timeStart.tv_nsec)/ 1000000; //计算操作消耗的线程时间,单位为us
printf("FBString thread time: %lf\n", res);
}

gettimeofday():获取当前系统时间

定义:获取当前系统时间,返回值为一个绝对值,从1970年到现在经过了多少秒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 原型定义
int gettimeofday ( struct timeval * tv , struct timezone * tz );

// timeval 结构体原型如下:
struct timeval
{
long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};

// timezone结构定义为:
struct timezone{
int tz_minuteswest; /*和Greenwich时间差了多少分钟*/
int tz_dsttime; /*日光节约时间的状态*/
};

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
#include<sys/time.h>
#include<unistd.h>
int main()
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv,&tz);
printf(“tv_sec:%d\n”,tv.tv_sec);
printf(“tv_usec:%d\n”,tv.tv_usec);
printf(“tz_minuteswest:%d\n”,tz.tz_minuteswest);
printf(“tz_dsttime:%d\n”,tz.tz_dsttime);
}

gmtime(): 转换时间

定义:将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm 返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义
struct tm *gmtime(const time_t *timep);

//tm 的定义
struct tm{
int tm_sec; //代表目前秒数, 正常范围为0-59, 但允许至61 秒
int tm_min; //代表目前分数, 范围0-59
int tm_hour; //从午夜算起的时数, 范围为0-23
int tm_mday; //目前月份的日数, 范围01-31
int tm_mon; //代表目前月份, 从一月算起, 范围从0-11
int tm_year; //从1900 年算起至今的年数
int tm_wday; //一星期的日数, 从星期一算起, 范围为0-6
int tm_yday; //从今年1 月1 日算起至今的天数, 范围为0-365
int tm_isdst; //日光节约时间的旗标
};

使用示例:

1
2
3
4
5
6
7
8
9
10
#include <time.h>
main(){
char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
time_t timep;
struct tm *p;
time(&timep);
p = gmtime(&timep);
printf("%d%d%d", (1900+p->tm_year), (1+p->tm_mon), p->tm_mday);
printf("%s%d;%d;%d\n", wday[p->tm_wday], p->tm_hour, p->tm_min, p->tm_sec);
}

asctime()

定义:将结构中的信息转换为真实世界的时间,以字符串的形式显示

1
char *asctime(const struct tm* timeptr)

使用示例:

1
2
3
4
5
6
7
8
9
#include <time.h>
main(){
time_t timep;
time (&timep);
printf("%s", asctime(gmtime(&timep)));
}
/* 输出结果:
Sat Oct 28 02:10:06 2000
*/