现在的位置: 首页 > 综合 > 正文

Linux下时间计算

2013年08月21日 ⁄ 综合 ⁄ 共 7404字 ⁄ 字号 评论关闭

一、 Linux的time命令
    Linux系统下统计程序运行实践最简单直接的方法就是使用time命令,文献[1, 2]中详细介绍了time命令的用法。此命令的用途在于测量特定指令执行时所需消耗的时间及系统资源等资讯,在统计的时间结果中包含以下数据:
       (1) 实际时间(real time):从命令行执行到运行终止的消逝时间;
       (2) 用户CPU时间(user CPU time):命令执行完成花费的系统CPU时间,即命令在用户态中执行时间的总和;
       (3) 系统CPU时间(system CPU time):命令执行完成花费的系统CPU时间,即命令在核心态中执行时间的总和。
    其中,用户CPU时间和系统CPU时间之和为CPU时间,即命令占用CPU执行的时间总和。实际时间要大于CPU时间,因为Linux是多任务操作系统,往往在执行一条命令时,系统还要处理其他任务。另一个需要注意的问题是即使每次执行相同的命令,所花费的时间也不一定相同,因为其花费的时间与系统运行相关。

二、间隔计数[4]
    上面介绍的time命令能测量特定进程执行时所消耗的时间,它是怎么做到的呢?
    操作系统用计时器来记录每个进程使用的累计时间,原理很简单,计时器中断发生时,操作系统会在当前进程列表中寻找哪个进程是活动的,一旦发现进程A正在运行立马就给进程A的计数值增加计时器的时间间隔(这也是引起较大误差的原因)。当然不是统一增加的,还要确定这个进程是在用户空间活动还是在内核空间活动,如果是用户模式,就增加用户时间,如果是内核模式,就增加系统时间。这种方法的原理虽然简单但不精确。如果一个进程的运行时间很短,短到和系统的计时器间隔一个数量级,用这种方法测出来的结果必然是不够精确的,头尾都有误差。不过,如果程序的时间足够长,这种误差有时能够相互弥补,一些被高估一些被低估,平均下来刚好。从理论上很难分析这个误差的值,所以一般只有程序达到秒的数量级时用这种方法测试程序时间才有意义。

    这种方法最大的优点是它的准确性不是非常依赖于系统负载。
    实现方法之一就是上面介绍的time命令,之二是使用tms结构体和times函数。
    在Linux中,提供了一个times函数,原型是
    clock_t times( struct tms * buf );
这个tms的结构体为
    struct tms
    {
        clock_t tms_utime;     //user time
        clock_t tms_stime;     //system time
        clock_t tms_cutime;    //user time of reaped children
        clock_t tms_cstime;    //system time of reaped children
    }

这里的cutime和cstime,都是对已经终止并回收的时间的累计,也就是说,times不能监视任何正在进行中的子进程所使用的时间。使用times函数需要包含头文件sys/times.h。

三、周期计数[4]

四、gettimeofday 函数计时[4]
    gettimeofday 是一个库函数,包含在time.h中。它的功能是查询系统时钟,以确定当前的日期和时间。相对于间隔计数的小适用范围和周期计数的麻烦性,gettimeofday是一个可移植性更好相对较准确的方法。它的原型如下:

    struct timeval
    {
        long tv_sec;      //秒域 seconds since the Epoch
        long tv_usec;     //微妙域
    }
    int gettimeofday( struct timeval *tv, NULL);

    这个机制呢,具体的实现方式在不同系统上是不一样的,而且具体的精确程度是和系统相关的:比如在Linux下,是用周期计数来实现这个函数的,所以和周期计数的精确度差不多,但是在Windows NT下,是使用间隔计数实现的,精确度就很低了。
    具体使用,就是在要计算运行时间的程序段之前和之后分别加上
    gettimeofday( &tvstart, NULL)
    …    
    gettimeofday( &tvend, NULL)
    (tvend.tv_sec-tvstart.tv_sec)+(tvend.tv_usec-tvstart.tv_usec)/1000000
就得到了以秒为单位的计时时间。

五、clock 函数
    clock 也是一个库函数,仍然包含在time.h中,函数原型是:
    clock_t clock( void );
功能:返回自程序开始运行的处理器时间,如果无可用信息,返回 -1。转换返回值若以秒计需除以 CLOCKS_PER_SECOND。(注:如果编译器是POSIX兼容的,CLOCKS_PER_SECOND定义为 1000000。)[5]
使用clock函数也比较简单:在要计时程序段前后分别调用clock函数,用后一次的返回值减去前一次的返回值就得到运行的处理器时间,然后再转换为秒。举例如下:

    clock_t starttime, endtime;
    double totaltime;
    starttime = clock();
    …
    endtime = clock();
    totaltime = (double)( (endtime - starttime)/(double)CLOCKS_PER_SEC );

六、 time函数
    在time.h中还包含另一个时间函数:time。文献[6]对其进行了详细的介绍。通过time()函数来获得日历时间(Calendar Time),其原型为:time_t time( time_t * timer )。通过difftime函数可以计算前后两次的时间差:double difftime( time_t time1, time_t time0 )。用time_t表示的时间(日历时间)是从一个时间点(例如:1970年1月1日0时0分0秒)到此时的秒数,则此函数的前后两次时间差也是以秒为单位。
    比如:

    time_t startT, endT;
    double totalT;
    startT = time( NULL );
    …
    endT = time( NULL );
    totalT = difftime( startT, endT);

关于此函数的其他应用请参见文献[6]。

总结:
    使用相应的方法,调用相应的函数,还需要关注它们可以表示的范围和精度,这样才能“挑肥拣瘦”。先来看看时间函数中经常用到的两个数据类型的定义:

    // clock_t 的定义
    #ifndef _CLOCK_T_DEFINED
    typedef long clock_t;
    #define _CLOCK_T_DEFINED
    #endif

    
    // time_t 的定义
    #ifndef _TIME_T_DEFINED
    typedef long time_t;
    #define _TIME_T_DEFINED
    #endif

long 型数据的取值范围是 [-2147483648 +2147483647]。所以,gettimeofday 函数取得的时间最大值为2147483647 + 2147483647 / 1000000 = 2147485794.483647 s,大约为68.096年;
clock函数取得的时间最大值为2147483647 / 1000000 = 2147.483647 s,大约为35.79分钟;
time函数取得的时间最大值为2147483647 s,大约为68年。
这里只是介绍Linux平台下c语言中计算程序运行时间的方法,它们各有利弊,依据自己的需要可以使用对应的方法。在Windows平台下还有其他计算程序运行时间的方法,在此不叙。

时间日期的概念,主要有以下几个:
Coordinated Universal TimeUTC):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich
Mean Time
GMT)。比如,中国内地的时间与UTC的时差为+8,也就是UTC+8。美国是UTC-5
Calendar Time:日历时间,是用从一个标准时间点到此时的时间经过的秒数来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是相对时间,但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。
epoch:时间点。时间点在标准C/C++中是一个整数,它用此时的时间和标准时间点相差的秒数(即日历时间)来表示。
clock tick:时钟计时单元(而不把它叫做时钟滴答次数),一个clock tick不是CPU的一个时钟周期,而是C/C++的一个基本计时单位。
我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法,无论是在结构定义,还是命名,都具有明显的C语言风格。下面,我将说明在C/C++中怎样使用日期的时间功能。
 
struct timeval
{
time_t tv_sec; //
susenconds_t tv_usec; //微秒 10 e-6
}
基准计算时间为1970年一月一日 UTC(世界标准时间) 0(Epoch)
time_t表示的时间(日历时间)是从一个时间点(例如:197011000秒)到此时的秒数。在time.h中,我们也可以看到time_t是一个长整型数:
#ifndef _TIME_T_DEFINED
typedef long time_t; /*
时间值
*/
#define _TIME_T_DEFINED /*
避免重复定义
time_t */
#endif
大家可能会产生疑问:既然time_t实际上是长整型,到未来的某一天,从一个时间点(一般是197011000秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对time_t数据类型的值来说,它所表示的时间不能晚于2038118191407秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual
C++
中采用了__time64_t数据类型来保存日历时间,并通过_time64()函数来获得日历时间(而不是通过使用32位字的time()函数),这样就可以通过该数据类型保存300111000秒(不包括该时间点)之前的时间。
 
下面的代码演示了如何获取当前系统时间并转换成字符串:
timeval tv;
gettimeofday(&tv,NULL);
std::cout<<ctime(&tv.tv_sec)<<endl;//将秒数转换成当前时间的字符串表示
tm及其相关API
time(取得目前的时间)
表头文件 #include<time.h>
定义函数 time_t time(time_t *t);
函数说明此函数会返回从公元197011日的UTC时间从000秒算起到现在所经过的秒数。如果t
并非空指针的话,此函数也会将返回值存到t指针所指的内存。
返回值成功则返回秒数,失败则返回((time_t)-1)值,错误原因存于errno中。
 
结构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//
从今年11日算起至今的天数,范围为0-365
int tm_isdst//
日光节约时间的旗标
};
示例如下:
time_t sec = time( 0 );//获取当前秒数(UTC)时间
struct tm tmbuf;
localtime_r( &sec, &tmbuf);//将秒数转换成当地时区时间
 
 
将字符串转换成time_t

char time_str[128] = "2005-10-18 9:37:45";
struct tm tmp;
strptime(time_str, "%Y-%m-%d %H:%M:%S", &tmp);
time_t tm = mktime(&tmp);

time_t转换成字符串:

time_t tm = time(NULL);
struct tm tmp;
localtime_r(&tm, &tmp);
strftime(buffer, bufferlen, "%Y-%m-%d %H:%M:%S", &tmp);

时间日期的概念,主要有以下几个:
Coordinated Universal TimeUTC):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间(Greenwich
Mean Time
GMT)。比如,中国内地的时间与UTC的时差为+8,也就是UTC+8。美国是UTC-5
Calendar Time:日历时间,是用从一个标准时间点到此时的时间经过的秒数来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是相对时间,但是无论你在哪一个时区,在同一时刻对同一个标准时间点来说,日历时间都是一样的。
epoch:时间点。时间点在标准C/C++中是一个整数,它用此时的时间和标准时间点相差的秒数(即日历时间)来表示。
clock tick:时钟计时单元(而不把它叫做时钟滴答次数),一个clock tick不是CPU的一个时钟周期,而是C/C++的一个基本计时单位。
我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法,无论是在结构定义,还是命名,都具有明显的C语言风格。下面,我将说明在C/C++中怎样使用日期的时间功能。
 
struct timeval
{
time_t tv_sec; //
susenconds_t tv_usec; //微秒 10 e-6
}
基准计算时间为1970年一月一日 UTC(世界标准时间) 0(Epoch)
time_t表示的时间(日历时间)是从一个时间点(例如:197011000秒)到此时的秒数。在time.h中,我们也可以看到time_t是一个长整型数:
#ifndef _TIME_T_DEFINED
typedef long time_t; /*
时间值
*/
#define _TIME_T_DEFINED /*
避免重复定义
time_t */
#endif
大家可能会产生疑问:既然time_t实际上是长整型,到未来的某一天,从一个时间点(一般是197011000秒)到那时的秒数(即日历时间)超出了长整形所能表示的数的范围怎么办?对time_t数据类型的值来说,它所表示的时间不能晚于2038118191407秒。为了能够表示更久远的时间,一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual
C++
中采用了__time64_t数据类型来保存日历时间,并通过_time64()函数来获得日历时间(而不是通过使用32位字的time()函数),这样就可以通过该数据类型保存300111000秒(不包括该时间点)之前的时间。
 
下面的代码演示了如何获取当前系统时间并转换成字符串:
timeval tv;
gettimeofday(&tv,NULL);
std::cout<<ctime(&tv.tv_sec)<<endl;//将秒数转换成当前时间的字符串表示
tm及其相关API
time(取得目前的时间)
表头文件 #include<time.h>
定义函数 time_t time(time_t *t);
函数说明此函数会返回从公元197011日的UTC时间从000秒算起到现在所经过的秒数。如果t
并非空指针的话,此函数也会将返回值存到t指针所指的内存。
返回值成功则返回秒数,失败则返回((time_t)-1)值,错误原因存于errno中。
 
结构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//
从今年11日算起至今的天数,范围为0-365
int tm_isdst//
日光节约时间的旗标
};
示例如下:
time_t sec = time( 0 );//获取当前秒数(UTC)时间
struct tm tmbuf;
localtime_r( &sec, &tmbuf);//将秒数转换成当地时区时间
 
 
将字符串转换成time_t

char time_str[128] = "2005-10-18 9:37:45";
struct tm tmp;
strptime(time_str, "%Y-%m-%d %H:%M:%S", &tmp);
time_t tm = mktime(&tmp);

time_t转换成字符串:

time_t tm = time(NULL);
struct tm tmp;
localtime_r(&tm, &tmp);
strftime(buffer, bufferlen, "%Y-%m-%d %H:%M:%S", &tmp);

抱歉!评论已关闭.