linux下程序时间测量方法

    技术2022-05-20  50

    程序遇到瓶颈时,无论是CPU、网络、内存还是磁盘I/O瓶颈,确定瓶颈产生的模块、函数都是首先要解决的问题。程序各个模块运行的时间可以一定程度上反映出程序的瓶颈所在,下面简单地总结几种linux常用的程序时间测量方法:

     

    1. linux下time命令的使用

     Linux下time命令可以获取到一个程序的执行时间,包括程序的实际运行时间(real time),以及程序运行在用户态的时间(user time)和内核态的时间(sys time)。

      使用方法:它的使用方法和前面讲过的strace类似,在待执行的命令前加上time即可。

       [leconte@localhost test]$ time ./myprogram

      real 0m0.020s

      user 0m0.000s

      sys 0m0.018s

     

      结果表明,程序实际运行时间0.020s,用户态运行时间接近0s,内核态运行时间0.018s。这是因为我们主要操作是使用文件相关的系统调用,程序大部分时间工作在内核态。

      需要注意的是,real并不等于user+sys的总和。real代表的是程序从开始到结束的全部时间,即使程序不占CPU也统计时间。而user+sys是程序占用CPU的总时间,因此real总是大于或者等于user+sys的。

     

      使用目的:通过此命令可以得到程序运行的时间,确定用户态与内核态的运行比例.

     

    2. 使用gettimeofday系统函数

    通过在程序的不同的位置记录系统时间,通过不同位置的时间差值来计算程序的不同的资源消耗。函数信息如下:

    #include <sys/time.h>

         #include <time.h>

       函数原型:int gettimeofday(struct timeval *tv, struct timezone *tz);

           struct timeval {

                   time_t         tv_sec;        /* seconds */

                   suseconds_t    tv_usec; /* microseconds */

           };

           struct timezone {

                   int tz_minuteswest; /* minutes W of Greenwich */

                   int tz_dsttime;     /* type of dst correction */

           };

     

    3.使用gprof和oprofile工具

    gprof是GNU工具之一,它在编译的时候在每个函数的出入口加入了profiling的代码,运行时统计程序在用户态的执行信息,可以得到每个函数的调用次数,执行时间,调用关系等信息,简单易懂。适合于查找用户级程序的性能瓶颈,对于很多时间都在内核态执行的程序,gprof不适合。

    oprofile也是一个开源的profiling工具,它使用硬件调试寄存器来统计信息,进行profiling的开销比较小,而且可以对内核进行profiling。它统计的信息非常的多,可以得到cache的缺失率,memory的访存信息,分支预测错误率等等,这些信息gprof是得不到的,但是对于函数调用次数,它是不能够得到的。

        简单来说,gprof简单,适合于查找用户级程序的瓶颈,而oprofile稍显复杂,但是得到的信息更多,更适合调试系统软件。

     

    gprof Quick Start

    gprof是gnu binutils工具之一,默认情况下linux系统当中都带有这个工具。

    使用 -pg 选项来编译hello.c,如果要得到带注释的源码清单,则需要增加 -g 选项。运行: gcc -pg -g -o hello hello.c

    运行应用程序: ./hello 会在当前目录下产生gmon.out文件

    使用gprof来分析gmon.out文件,需要把它和产生它的应用程序关联起来:

    gprof hello gmon.out -p 得到每个函数占用的执行时间

    gprof hello gmon.out -q 得到call graph,包含了每个函数的调用关系,调用次数,执行时间等信息。

    gprof hello gmon.out -A 得到一个带注释的“源代码清单”,它会注释源码,指出每个函数的执行次数。这需要在编译的时候增加 -g选项。

     

    oprofile Quick Start

         oprofile是sourceforge上面的一个开源项目,在2.6内核上带有这个工具,好像只有smp系统才有。比较老的系统,需要自己安装,重新编译内核。

         oprofile是一套工具,分别完成不同的事情。

     

    op_help: 列出所有支持的事件。

    opcontrol:设置需要收集的事件。

    opreport: 对结果进行统计输出。

    opannaotate:产生带注释的源/汇编文件,源语言级的注释需要编译源文件时的支持。

    opstack:    产生调用图profile,但要求x86/2.6的平台,并且linux2.6安装了call-graph patch

    opgprof:    产生如gprof相似的结果。

    oparchive: 将所有的原始数据文件收集打包,可以到另一台机器上进行分析。

    op_import: 将采样的数据库文件从另一种abi转化成本地格式。    运行oprofile需要root权限,因为它要加载profile模块,启动 oprofiled后台程序等。所以在运行之前,就需要切换到root。

    opcontrol --init 加载模块,mout /dev/oprofile 创建必需的文件和目录

    opcontrol --no-vmlinux 或者 opcontrol --vmlinux=/boot/vmlinux-`uname -r` 决定是否对kernel进行profiling

    opcontrol --reset 清楚当前会话中的数据

    opcontrol --start 开始profiling

    ./hello 运行应用程序,oprofile会对它进行profiling

    opcontrol --dump 把收集到的数据写入文件

    opcontrol --stop 停止profiling

    opcotrol -h 关闭守护进程oprofiled

    opcontrol --shutdown 停止oprofiled

    opcontrol --deinit 卸载模块

    常用的是3→7这几个过程,得到性能数据之后,可以使用opreport, opstack, opgprof, opannotate几个工具进行分析,我常用的是opreport, opannotate进行分析。

    opreport使用 http://oprofile.sourceforge.net/doc/opreport.html

    opannotate使用 http://oprofile.sourceforge.net/doc/opannotate.html

    opgprof使用 http://oprofile.sourceforge.net/doc/opgprof.html

    最常用的是opreport,这个可以给出image和symbols的信息,比如我想得到每个函数的执行时间占用比例等信息,用来发现系统性能瓶颈。opannotate可以对源码进行注释,指出哪个地方占用时间比较多。常用命令如下:

    opreport -l /bin/bash --exclude-depand --threshold 1 , 用来发现系统瓶颈。

    opannotate --source --output-dir=annotated /usr/local/oprofile-pp/bin/oprofiled

    opannotate --source --base-dirs=/tmp/build/libfoo/ --search-dirs=/home/user/libfoo/ --output-dir=annotated/ /lib/libfoo.so

     

    4. 使用truss、strace或ltrace诊断软件

    truss 和strace用来跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;而strace最初是为SunOS系统编写的,ltrace 最早出现在GNU/Debian Linux中。这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。 

         你不仅可以从命令行调试一个新开始的程序,也可以把truss、strace或ltrace绑定到一个已有的PID上来调试一个正在运行的程序。三个调试工具的基本使用方法大体相同,下面仅介绍三者共有,而且是最常用的三个命令行参数: 

    -f :除了跟踪当前进程外,还跟踪其子进程。 

    -o file :将输出信息写到文件file中,而不是显示到标准错误输出(stderr)。 

    -p pid :绑定到一个由pid对应的正在运行的进程。此参数常用来调试后台进程。 

     

    -r 打印出相对时间关于,,每一个系统调用. 

    -t 在输出中的每一行前加上时间信息. 

    -tt 在输出中的每一行前加上时间信息,微秒级. 

    -ttt 微秒级输出,以秒了表示时间. 

    -T 显示每一调用所耗的时间. 

     

    5. 间隔计数

           操作系统用计时器来记录每个进程使用的累计时间,原理很简单,计时器中断发生时,操作系统会在当前进程列表中寻找哪个进程是活动的,一旦发现进程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。

     

    6. 周期计数

           为了给计时测量提供更高的准确度,很多处理器还包含一个运行在始终周期级别的计时器,它是一个特殊的寄存器,每个时钟周期它都会自动加1。这个周期计数器呢,是一个64位无符号数,直观理解,就是如果你的处理器是1GHz的,那么需要570年,它才会从2的64次方绕回到0,所以你大可不必考虑溢出的问题。但是这种方法是依赖于硬件的。首先,并不是每种处理器都有这样的寄存器的;其次,即使大多数都有,实现机制也不一样,因此,我们无法用统一的,与平台无关的接口来使用它们。这下,就要使用汇编了。当然,在这里实际用的是C语言的嵌入汇编:

     

    void counter( unsigned *hi, unsigned *lo )

    {

    asm(”rdtsc; movl %

    转载请注明原文地址: https://ibbs.8miu.com/read-2227980.html

    最新回复(0)