深入理解计算机系统--测量程序执行时间

本文探讨了两种主要的时间测量方法:间隔计数与周期计数。详细介绍了这两种方法的特点及适用场景,并对比了gettimeofday()与clock_gettime()两种系统调用的不同之处。

 

理论基础

在进行系统时间获取并用于程序性能分析时,我们需要考虑精确而有效的时间获取方法。

 

在《深入理解计算机系统》这本书中谈到,目前进行时间测量的系统接口和方法有两种:

    通过间隔计数的方法:

        OS维护着每个进程使用的用户时间量和系统时间量的计数值,当计时器中断发生时,OS会确定哪个进程是活动的,并且对那个进程的一个计数值增加计时器间隔时间。这时会根据进程的状态,如果是内核态就增加系统时间,否则增加用户时间。

        这种计时方式并不管在这个时间段(两个计时器中断之间)内到底用了多少时间,所以不够准确。但是从长时间考虑,这个平均值还是可以接受的。

    利用周期计数器的方法:

        很多CPU包含了一个运行在时钟周期级的计时器。这个计时器其实是个特殊的寄存器,每个时钟周期会加1。这个工具可以用来测量一个程序执行中两个不同点之间经过的时间。就是基于系统时间点的测量,而在这两点之间的情况并不关心。这样导致了多任务系统上,这种计时器只能用于对比而无法直接表示进程时间消耗。所以其受上下文切换、高速缓存的影响。

通过上面的比较和介绍,我们可以看到,两种方法各有优劣、是从不同的方向来进行时间测量的。

所以,这本书在章节后面给出了一个协议:

    如果程序X预期的运行时间很长(运行时间远远大于间隔计数的时间段),可以使用间隔计数的方式。

    如果X预期运行时间大概在0.01~1.0s之间,那么在负载很轻的系统上,就需要使用周期计时的方式。在这里强调负载轻,主要原因是害怕上下文切换等因素的影响。

    如果X预期运行时间小于0.01s,那么就使用周期计时的方式。这时候,由于时间很短,不用担心负载问题,上下文切换等因素起到的影响就很小了。

 

下面分析我们通常使用的:

-----------------------------------

clock_gettime:

    .save   {r4, r7}

    stmfd   sp!, {r4, r7}

    ldr     r7, =__NR_clock_gettime

    swi     #0

    ldmfd   sp!, {r4, r7}

    movs    r0, r0

    bxpl    lr

    b       __set_syscall_errno

    .fnend

-----------------------------------

gettimeofday:

    .save   {r4, r7}

    stmfd   sp!, {r4, r7}

    ldr     r7, =__NR_gettimeofday

    swi     #0

    ldmfd   sp!, {r4, r7}

    movs    r0, r0

    bxpl    lr

    b       __set_syscall_errno

    .fnend

-----------------------------------

这两个函数都是直接进行了系统调用。

 

关于时间源这类的时间调用是复杂的,我也没有做这方面工作的经验,不敢乱言。

使用说明请参考: http://www.360doc.com/content/11/0715/09/1317564_133662142.shtml

gettimeofday()提供了微秒级的精确度  

    1、头文件 <time.h>  

    2、函数原型  

    int gettimeofday(struct timeval *tv, struct timezone *tz);   

 

    gettimeofday()会把目前的时间由tv所指的结构返回,当地时区的信息则放到tz所指的结构中(可用NULL)。  

    参数说明:  

        timeval结构定义为:  

        struct timeval  

        {  

            long tv_sec; /*秒*/  

            long tv_usec; /*微秒*/  

        };  

        timezone 结构定义为:  

        struct timezone  

        {  

            int tz_minuteswest; /*和Greenwich 时间差了多少分钟*/  

            int tz_dsttime; /*日光节约时间的状态*/  

        };  

        上述两个结构都定义在/usr/include/sys/time.h。tz_dsttime 所代表的状态如下  

            DST_NONE /*不使用*/  

            DST_USA /*美国*/  

            DST_AUST /*澳洲*/  

            DST_WET /*西欧*/  

            DST_MET /*中欧*/  

            DST_EET /*东欧*/  

            DST_CAN /*加拿大*/  

            DST_GB /*大不列颠*/  

            DST_RUM /*罗马尼亚*/  

            DST_TUR /*土耳其*/  

            DST_AUSTALT /*澳洲(1986年以后)*/  

 

    返回值: 成功则返回0,失败返回-1,错误代码存于errno。附加说明EFAULT指针tv和tz所指的内存空间超出存取权限。  

 

    #include<stdio.h>  

    #include<time.h>  

    int main(void)  

    {  

        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);  

 

        return 0;  

    }  

 

clock_gettime( ) 提供了纳秒级的精确度  

    1、头文件 <time.h>  

    2、编译&链接。在编译链接时需加上 -lrt ;因为在librt中实现了clock_gettime函数  

    3、函数原型  

    int clock_gettime(clockid_t clk_id, struct timespect *tp);  

        参数说明:  

        clockid_t clk_id 用于指定计时时钟的类型,有以下4种:  

            CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户该成其他,则对应的时间相应改变  

            CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响  

            CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间  

            CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间  

        struct timespect *tp用来存储当前的时间,其结构如下:  

            struct timespec  

            {  

                time_t tv_sec; /* seconds */  

                long tv_nsec; /* nanoseconds */  

            };  

        返回值。0成功,-1失败  

 

    #include<stdio.h>  

    #include<time.h>  

    int main()  

    {  

        struct timespec ts;  

 

        clock_gettime(CLOCK_REALTIME, &ts);  

        printf("CLOCK_REALTIME: %d, %d", ts.tv_sec, ts.tv_nsec);  

 

        clock_gettime(CLOCK_MONOTONIC, &ts);//打印出来的时间跟 cat /proc/uptime 第一个参数一样  

        printf("CLOCK_MONOTONIC: %d, %d", ts.tv_sec, ts.tv_nsec);  

 

        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);  

        printf("CLOCK_PROCESS_CPUTIME_ID: %d, %d", ts.tv_sec, ts.tv_nsec);  

 

        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);  

        printf("CLOCK_THREAD_CPUTIME_ID: %d, %d", ts.tv_sec, ts.tv_nsec);  

 

        printf("/n%d/n", time(NULL));  

 

        return 0;  

    }  

    /proc/uptime里面的两个数字分别表示:   

    the uptime of the system (seconds), and the amount of time spent in idle process (seconds).   

    把第一个数读出来,那就是从系统启动至今的时间,单位是秒  


 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值