linux cpu 时间,【Linux】CPU时间与处理器耗时

文章结构:

CPU时间(CPU TIME)是指当应用进程启动后,占用CPU进行计算所进行的时间绝对值,或叫时间点。如果进程进入中断、挂起、休眠等行为时,是不占用CPU的,所以CPU时间并不会跟着增加,且进程恢复运行后所获得的CPU时间值与运行中断之前的时间值是连续的,并不会因为运行的暂停而导致CPU时间的跳跃。这是和普通的日常时间值不一样的点。另一个不同的点是CPU时间并不是以秒为单位的,而是处理器的时钟周期为单位。

获取CPU时间的函数#include

clock_t clock(void)

该函数返回CPU时间,CPU时间并不一定会从0开始,所以要计算进程的一段代码所消耗的CPU时间记录需要在代码前后获取各自的起始和终止CPU时间,两者之差即为所消耗的CPU时间,即为处理器耗时(Processor Time)。要将clock_t转换为秒数,需要除以宏CLOCKS_PER_SEC生成的值,即为秒数。但要注意的是由于在不同的系统中clock_t或CLOCKS_PER_SEC可能为int也可能为浮点型数值,所以在计算时需要在计算过程中并其强制转换为double以保证计算结果。另外需要注意的是clock()函数所返回的值在32位系统上每72分钟就会恢复原始值一次。clock()如果执行错误,则会返回-1。

获取CPU时间与更详细的进程时间还有另外一个函数:#include

clock_t times(struct tms *buf);

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 */

};

函数times()会将进程的处理器时间分类。在参数tms中各字段含义为:tms_utime:用户层处理器耗时。即执行该进程的实现代码所消耗的CPU时间和。tms_stime:系统层处理器耗时。即该进程实现的代码调用了系统内核代码,由这部分内核代码执行所消耗的CPU时间和。tms_cutime:子进程用户层处理器耗时。即如果当前进程通过调用system()或fork()函数导致产生新的子进程(数量前后可能为多个),则记录所有子进程的实现代码所消耗的CPU时间和。tms_cstime:子进程用户层处理器耗时。即如果当前进程通过调用system()或fork()函数导致产生新的子进程(数量前后可能为多个),则记录所有子进程实现的代码调用了系统内核代码,由这部分内核代码执行所消耗的CPU时间和。

函数times()所返回的值单位都是时钟滴哒(clock tick)数。要获取每秒钟的时钟滴哒可以调用函数sysconf(_SC_CLK_TCK);来获取。为了保证精度,在计算耗时时,也要在计算过程中将参与的计算值转换为double以保证计算结果。函数times()所返回的值表示过去某个时间点到函数调用此刻的时间消耗长度。所以进程的中断、挂起、休眠等行为仍会导致返回值的持续增加。这一点是与clock()的不同所在。通常也利用这个特点来计算进程的前后自然时间耗时时长。在计算时长方面,times()通过精确到秒以下(可能是毫秒,因为是double类型的计算),但缺点是参数buf必须不为空;difftime()由于其参数设定的原因只能精确到秒,能力是最弱的;gettimeofday()所获取的timeval则可精确到微秒,只是计算时间差值时麻烦一点。

除了在代码中调用函数来计算各种耗时以外,还可能通过命令time来显示耗时统计。Time命令原型如下:

time [options] command [arguments...]

options:time命令的可选参数,只接受-p,表示为按标准格式输出。如下:

170da7664c18e9a9cc6f26612e4767aa.png

real: 命令`date`的全部运行时间时长。

user: 命令`date`(包括其子进程,如果有的话)的用户层处理器耗时。

sys : 命令`date`(包括其子进程,如果有的话)的系统层处理器耗时。

command:time要统计的命令。[arguments]:command命令所接受的参数。

命令Time的输出是可以自定义的,通过设置环境变量TIMEFORMAT指定输出所要的格式化结果。另从man手册中查看Time还具有查看CPU占用率、I/O数据量等功能(可是按文档在Mac上来操作没有成功呀…)

接下来的代码将演示:

#include #include #include

pid_t getpid(void); // 获取进程id

pid_t getppid(void); // 获取父进程id

pid_t gettid(void){ // 获取线程id

return syscall(SYS_gettid);

}

这里值得提一下的是getttid()是系统内核非对外公开的函数。所以这里根据其源码实现利用syscall()方法来获取线程id。

#include

unsigned int sleep(unsigned int seconds);

参数seconds表示线程的休眠时间,单位为秒。在指定的休眠时间内,线程会暂停工作直至时间到期或被中断。

为了测试times()中的tms_cutime和tms_cstime,需要创建一个子进程,这里用到system()函数。#include

int system(const char *command);

参数command为命令,可以是可执行程序路径也可以是系统支持的命令。system()函数会调用fork()去创建一个子进程并执行参数command。在子进程执行期间,父进程所调用的线程是被中断的,不会再进行任何操作直至等到子进程全部执行完毕。子进程所产生的日志输出也将在结束之后才会被打印出来。

示例代码中,通过在程序的开头、进行中、结束各个阶段执行clock()来获取进程当前的CPU时间点,通过减法操作最终得到每个阶段的处理器耗时。

执行完sleep()的前后CPU时间对比可以验证CPU时间是进程所消耗处理器而产生的连续的时间集合。CPU用了多少就是多少,而与正常的时间流逝无关。执行完system()的前后CPU时间对比可以验证子进程的CPU时间并不会被计算在父进程中,可以通过times()区分。

clock_t c_start = clock();

sleep(2); // 进程进入休眠

clock_t c_mid01 = clock();

doCopyFile(); // 进程进入繁忙的工作状态

clock_t c_mid02 = clock();

system(“”); // 生成子进程

clock_t c_end = clock();

doCopyFile()函数是复制文件的代码实现,本示例中所复制的文件大小为35M,耗时大概3.5s左右。具体复制代码实现可以查看之前的文章:【C/C++】文件创建、打开、读、写、复制、关闭、删除等操作汇总

获取耗时详情的前提是要获取每秒时钟滴哒数;而详情则是通过struct tms的前后对比得出的时间差:

// process_start与后面的process_end用于计算整个进程的耗时

clock_t process_start = times(&sys_t_start);

// 获取每秒的时钟滴哒数

long tck = sysconf(_SC_CLK_TCK);

... ... ...

struct tms sys_t_end;

clock_t process_end = times(&sys_t_end);

printf("4. sys time: user time=%f, stime=%f, cutime=%f, cstime=%f \n",

(sys_t_end.tms_utime - sys_t_start.tms_utime)/(double)tck,

(sys_t_end.tms_stime - sys_t_start.tms_stime)/(double)tck,

(sys_t_end.tms_cutime - sys_t_start.tms_cutime)/(double)tck,

(sys_t_end.tms_cstime - sys_t_start.tms_cstime)/(double)tck);

printf("5. real elapsed time=%f \n", ((process_end - process_start)/(double)tck));

最后看代码的运行结果吧,如下图:

e04abe80fb4f96403ea13ae1ee6b6556.png

图中,real elapsed time=9.250000与time命令所打印出来的real 0m9.249s相符;user time=3.360000 , cutime=3.380000两者之和与user 0m6.757s相符;stime=0.120000 cstime=0.120000两者之和与sys 0m255s相符。数值上不是绝对相等,代码内的自我计算总是会少于time的值,因为代码内是计算完了之后要打印的,这部分打印及退出的时间是代码的盲区,而time命令能统计到。

以下为实现代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

#include

#include

#include

#include

#include

#include

#include

#include

#include

pid_t gettid(

void){

return syscall(SYS_gettid);

}

int main(

int argc,

char **argv) {

// author : sodino@qq.com

struct tms sys_t_start;

clock_t process_start = times(&sys_t_start);

clock_t c_start = clock();

times(&sys_t_start);

printf(

" pid=%d ppid=%d tid=%d \n", getpid(), getppid(), gettid());

long tck = sysconf(_SC_CLK_TCK);

sleep(

2);

clock_t c_mid01 = clock();

double diff = (c_mid01 - c_start)/(

double)CLOCKS_PER_SEC;

printf(

"1. after sleep(2) consume clock time=%f \n", diff);

doCopyFile();

clock_t c_mid02 = clock();

diff = (

double)(c_mid02 - c_start)/CLOCKS_PER_SEC;

printf(

"2. after doCopyFile() consume clock time=%f \n", diff);

// 这里会产生新的进程

system(

"/Users/sodino/workspace/xcode/Define/Define/t_copy.out");

clock_t c_end = clock();

diff = (

double)(c_end - c_start)/CLOCKS_PER_SEC;

printf(

"3. after system() consume clock time=%f \n", diff);

struct tms sys_t_end;

clock_t process_end = times(&sys_t_end);

printf(

"4. sys time: user time=%f, stime=%f, cutime=%f, cstime=%f \n",

(sys_t_end.tms_utime - sys_t_start.tms_utime)/(

double)tck,

(sys_t_end.tms_stime - sys_t_start.tms_stime)/(

double)tck,

(sys_t_end.tms_cutime - sys_t_start.tms_cutime)/(

double)tck,

(sys_t_end.tms_cstime - sys_t_start.tms_cstime)/(

double)tck);

printf(

"5. real elapsed time=%f \n", ((process_end - process_start)/(

double)tck));

return EXIT_SUCCESS;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值