计算程序运行时间(time_t, clock_t)
转载自:http://blog.chinaunix.net/uid-23208702-id-75182.html
计算程序运行时间(time_t, clock_t)-whyliyi-ChinaUnix博客
我们有时需要得到程序的运行时间,但我们也要知道,根本不可能精确测量某一个程序运行的确切时间 [3] ,文献 [4] 中说的很明白,现摘录如 下。
我们平时常用的测量运行时间的方法并不是那么精确的,换句话说,想精确获取程序运行时间并不是那么 容易的。也许你会想,程序不就是一条条指令么,每一条指令序列都有固定执行时间,为什么不好算?真实情况下,我们的计算机并不是只运行一个程序的,进程的 切换,各种中断,共享的多用户,网络流量,高速缓存的访问,转移预测等,都会对计时产生影响。文献 [4] 中还提到:对于进程调度来 讲,花费的时间分为两部分,第一是计时器中断处理的时间,也就是当且仅当这个时间间隔的时候,操作系统会选择,是继续当前进程的执行还是切换到另外一个进 程中去。第二是进程切换时间,当系统要从进程 A 切换到进程 B 时,它必须先进入内核模式将进程 A 的状态 保存,然后恢复进程 B 的状态。因此,这个切换过程是有内核活动来消耗时间的。具体到进程的执行时间,这个时间也包括内核 模式和用户模式两部分,模式之间的切换也是需要消耗时间,不过都算在进程执行时间中了。那么有哪些方法能统计程序的运行时间呢?通过查找一些资料并结合自己的实践体会,摘录和总结了下面 几种方法。
一、 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 timeclock_t tms_stime; //system timeclock_t tms_cutime; //user time of reaped childrenclock_t tms_cstime; //system time of reaped children
}
这里的 cutime 和 cstime ,都是对已经终止并回收的时间的累计,也就是说, times 不能监视任何正在进行中的子进程所使用的时间。使用 times 函数需要包含头文件 sys/times.h 。
三、周期计数 [4]
为了给计时测量提供更高的准确度,很多处理器还包含一个运行在始终周期级别的计时器,它是一个特殊 的寄存器,每个时钟周期它都会自动加 1 。这个周期计数器呢,是一个 64 位无 符号数,直观理解,就是如果你的处理器是 1GHz 的,那么需要 570 年,它才会从 2 的 64 次方绕回到 0 ,所以 你大可不必考虑溢出的问题。但是这种方法是依赖于硬件的。首先,并不是每种处理器都有这样的寄存器的;其次,即使大多数都有,实现机制也不一样,因此,我 们无法用统一的,与平台无关的接口来使用它们。这下,就要使用汇编了。当然,在这里实际用的是 C 语言 的嵌入汇编:
- void counter( unsigned *hi, unsigned *lo ){asm(”rdtsc; movl %%edx,%0; movl %%eax, %1″
- “=r” (*hi), “=r” (*lo)
: - “%edx”, “%eax”);
}
第一行的指令负责读取周期计数器,后面的指令表示将其转移到指定地点或寄存器。这样,我们将这段代码封装到函数中,就可以在需要测量 的代码前后均加上这个函数即可。最后得到的 hi 和 lo 值都是两个,除了相减得到间隔值外,还要进行一些处理,在此 不表。
不得不提出的是,周期计数方式还有一个问题,就是我们得到了 两次调用 counter 之间总的周期数,但我们不知道是哪个进程使用了这些周期,或者说处理器是在内核还是在用户模式 中。间隔计数的好处就是它是操作系统控制给进程计时的,我们可以知道具体哪个进程呢个模式;但是周期计数只测量经过的时间,他不管是哪个进程使用的。所 以,用周期计数的话必须很小心。举个例子:double time(){start_counter();p();get_counter();
}
这样一段程序,如果机器的负载很重,会导致 p 运行 时间很长,而其实 p 函数本身是不需要运行这么长时间的,而是上下文切换等过程将它的时间拖长了。
而且,转移预测和高速缓存的命中率,对这个计数值也会有影响。通常情况下,为了减少高速缓存不命中 给我们程序执行时间带来的影响,可以执行这样的代码:double time_warm(void){p();start_counter();p();get_counter();
}
它让指令高速缓存和数据高速缓存都得到了 warm-up 。
接下来又有问题。如果我们的应用是属于那种每次执行都希望访问新的数据的那种呢?在这种情况下,我 们希望让指令高速缓存 warm-up ,而数据高速缓存不能 warm-up ,很明显, time-warm 函数低估我们的运行时间了。进一步修改:double time_cold( void ){p();clear_cache();start_counter();p();get_counter();
}
注意,程序中加入了一个清除数据缓存的函数,这个函数的具体实现很简 单,依情况而定,比如举个例子:
volatile int tmp;static int dummy[N]; //N 是需要清理缓存的字节数void clear_cache( void ){int i, sum = 0;for( i=1; idummy[i] = 2;for( i=1; isum += dummy[i];tmp = sum;
}
具体原理很简单,定义一个数组并在其上执行一个计算,计算过程中的数据会覆盖高速数据缓存中原有的数 据。每一次的 store 和 load 都会让高速数据缓存 cache 这个数组,而定义为 volatile 的 tmp 则保证这段代码不会被优化。
这样做,是不是就万无一失了呢?不是的,因为大多数处理器, L2 高速缓存是不分指令和数据的,这样 clear_cache 会让所有 p 的指令也被清除,只不过: L1 缓存中的指令还会保留而已。其实上面提到的诸多原因,都是我们不能控制的,我们无法控制让高速缓存去加载什么,不去加载什么, 加载时去掉什么。保留什么。而且,这些误差通常都是会过高估计真实的运行时间。那么具体使用时,有没有什么办法来改善这种情况呢?有,就是 The K-Best Measurement Scheme 。这其实很麻烦,所以在具体实践中都不用它。
四、 gettimeofday 函数计时 [4]
gettimeofday 是一个库函数,包含在 time.h 中。它的功能是查询系统时钟,以确定当前的日期和时间。相对于间隔计数的小适用范围和周期计数的麻烦性, gettimeofday 是一个可移植性更好相对较准确的方法。它的原型如下:struct timeval{long tv_sec; // 秒 域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 平台下还有其他计算程序运 行时间的方法,在此不叙。
参考文献
[1] “ linux time 命令详解”, http://www.admin99.net/read.php/185.htm ;
[2] “ Linux 命令详解—— time ”,
http://blog.csdn.net/thinkerABC/archive/2006/04/01/647272.aspx ;
[3] “测量程序运行时间的几种方法”, http://oss.lzu.edu.cn/blog/article.php?tid_905.html ;
[4] “如何精确测量程序运行时间”, http://www.forwind.cn/2008/05/10/measure-time-preciely/ ;
[5] “ clock ”, http://blog.csdn.net/xxyakoo/archive/2008/12/17/3539590.aspx ;
[6] “ c 语言对时间的处理函数和计时的实 现”,
http://blog.csdn.net/adm_qxx/archive/2007/05/02/1594788.aspx 。
相关文章:
又一年5.20,用Python助力程序员脱单大攻略(视频版)
作者 | 写代码的明哥来源 | Python编程时光(ID: Cool-Python)情人节年年有,但今年的 5.20 要比以往的更有意义。2020.05.20 ,爱你爱你我爱你,如果再卡个时间(13:14),那就是 爱你爱你…

pthred()多线程计算派
实验一:计算π问题描述实验提供了两种计算方法,一种使用积分方法,另一种采用随机数方法。本报告中采用积分方法。计算公式:程序流程图:(图1)函数流程图(图2)一组实验数据,计算规模:500,000,000性…

使用最小堆优化Dijkstra算法
OJ5.2很简单,使用priority_queue实现了最小堆竟然都过了OJ……每次遇到relax的问题时都简单粗暴地重新push进一个节点…… 然而正确的实现应该是下面这样的吧,关键在于swap堆中元素时使用pos数组存储改变位置后的编号为k的节点对应在堆中的位置。下面这种…

C语言编程技巧-signal(信号机制)
http://blog.sina.com.cn/s/blog_6a1837e90100v1vc.html

第一课:网络参考模型OSI
网络参考模型OSI(一):模型提出目的:开放系统互连。使各个厂商的设备可以很好的互连、互通、互操作。(二):各层功能(1):物理层:负责链路上bit流的传输。(bit流显著的特点是,不支持格式或者结构)。…
在线直播 | 是事实还是贩卖焦虑?IT行业也偏爱“小鲜肉”
几年前曾看过这样一篇报道:Java 之父求职被嫌年纪大,硅谷公司现在喜欢“小鲜肉”,不爱“老古董”。Java之父 James Gosling 在 Facebook 上发表了他所遭遇的年龄歧视:我曾在面试的时候被 HR 告知,“通常我们不招你这种…

eclipse 代码中突然出现特殊字符
在写代码的时候,不知道点到了 eclipse 的哪个属性,代码中就出现了一些特殊字符,也不能删除。 请问,在 eclipse 中该怎么设置,才能将这些字符去掉。 如下图所示: 解决方法: 选择Window->Preferences->…

如何优化数据中心虚拟机布局
当前已经有很多组织将服务器虚拟化技术引入到生产中,这么做是有道理的,特别是在当前经济并不景气的情况下,因为服务器虚拟化技术可以在服务器硬件,机架空间,电力消耗和制冷方面为组织节省开支。 但为了实现服务器虚…

回归——同步更新github.io
回归 已经有好长时间没写博客了,可能我比较懒,不太乐于分享,我觉得这个是一个很不好的习惯。但我坚信:Sharing changes the world! 最近搭建了自己的个人独立博客,基于Github Pages的,所以打算以后同步更…
支持量子机器学习,王海峰发布最新百度飞桨全景图
出品 | AI科技大本营(ID:rgznai100)刚刚,WAVE SUMMIT 2020深度学习开发者峰会上,百度CTO王海峰开场即披露了一组飞桨数据:飞桨累计开发者数量已超过190万,服务企业数量达8.4万家,发布模型数量已…

NPOI读写Excel
1、整个Excel表格叫做工作表:WorkBook(工作薄),包含的叫页(工作表):Sheet;行:Row;单元格Cell。 2、NPOI是POI的C#版本,NPOI的行和列的index都是从…

我的vim捣鼓之路
2016-06-13 更新 绑定独立博客到域名rebootcat.com 2016-06-12 更新文中的几个链接错误,google search报错 前言 从大二的时候就开始接触Linux了,从而也接触了vi,对的,当时对这些还不太了解,不知道还有个vim,真的觉得…
代码写对了还挂了?程序媛小姐姐从 LRU Cache 带你看面试的本质
来源 | 码农田小齐责编 | Carol 前言在讲这道题之前,我想先聊聊「技术面试究竟是在考什么」这个问题。技术面试究竟在考什么在人人都知道刷题的今天,面试官也都知道大家会刷题准备面试,代码大家都会写,那面试为什么还在考这些题&…

广船国际股份有限公司OA项目
2003年的老案例: 背景 广船国际股份有限公司是由原中国船舶工业总公司属下国有企业广州造船厂在1993年改组、在上海和香港同期上市的股份有限公司,公司享有自营进出口权。 广船国际于2002年3月通过评标后选定采用iOffice.net信息管理平台作为信息化建设…

注册表----修改Win7登录界面
在进行操作前,需要准备好背景图片。对背景图片的要求有三点: (1)图片必须是JPG格式; (2)必须将图片命名为backgroundDefault; (3)图片的体积必须小于256KB。 按下【WinR】…

定义自己的rm command
rm 是一个很危险的命令,别人一直说,我并没有在意,直到有一天一个不小心,忘记当前目录的位置,手贱的使用了rm命令,结果花了半天也没有把那些重要资料给恢复过来。所以还是有必要给自己定义一个不那么危险的r…
出任 Twitter 独立董事,AI 女神李飞飞的传奇人生
作者 | 年素清责编 | 伍杏玲出品 | 程序人生(ID:coder_life) 近日,Twitter宣布任命斯坦福大学计算机科学教授、前谷歌副总裁李飞飞为董事会独立董事。李飞飞本人表示:“推特是科技连接世界的一个重要平台,…

apache ab压力测试
2019独角兽企业重金招聘Python工程师标准>>> ab的原理:ab命令会创建多个并发访问线程,模拟多个访问者同时对摸一个URL地址进行访问。它的测试目标是基于URL的,因此它既可以用来测试apache的负载压力,也可以测试nginx、…

我的个人博客搭建记录
6/13更新 绑定个人博客到域名 rebootcat.com 前言 本篇博客旨在备忘,并记录了自己折腾了3,4天后顺利搭建自己的个人博客过程中碰到的一部分问题。 搭建个人独立博客有很多种方法,我暂时采用的是基于github Pages的免费博客,博客框架采用he…

oracle中创建触发器
从csdn上面看到一个如何创建触发器的问题,感觉自己很有必要保存学习,特写下来:条件:现有A、B两张表A: 工号 姓名 密码 性别 年龄 。。。B: 工号 姓名 密码当对A表中的“密码”字段进行修改时,B表中的“密码…
量子计算与AI“双拳”出击,他们锁定38种潜在抗疫药物
作者 | Just出品 | AI科技大本营(ID:rgznai100)医药研发行业有一个“三个十”的说法,即一种药物的发现需要投入十年以上的时间,花费十多亿美元,最后获得10%的成功率。也就是说,医药研发需要花费很长时间&am…

Android官方开发文档Training系列课程中文版:OpenGL绘图之应用投影与相机视图
原文地址:http://android.xsoftlab.net/training/graphics/opengl/projection.html##transform 在OpenGL ES环境中,投影相机View可以将所绘制的图形模拟成现实中所看到的物理性状。这种物理模拟是通过改变对象的数字坐标实现的: 投影 - 这基于…
Python分析101位《创造营2020》小姐姐,谁才是你心中的颜值担当?
来源 | CDA 数据分析师责编 | Carol Show me data,用数据说话。今天我们聊一聊《创造营2020》各个小姐姐,点击下方视频,先睹为快: 最近可以追的综艺真是太多了,特别是女团选秀节目。之前我们刚聊过《青春有你2》&…

体验Remix——安卓电脑
第一次听说Android-X86 以前玩唱吧的时候接触过PC上的安卓模拟器,不过这个只是一个软件,效果毕竟不好,想要把电脑变成安卓手机,还差远了。 然后,前段时间一直纠结要不要换个手机,我现在的华为小6已经跟我…

重置 microsoft visual studio窗口
“工具”->“导入导出设置”—>“重置所有设置”,在这个向导中可以重置编译环境的!转载于:https://www.cnblogs.com/qiantuwuliang/archive/2011/05/31/2064825.html

排序算法总结之堆排序
一,堆排序介绍 堆是一个优先级队列,对于大顶堆而言,堆顶元素的权值最大。将 待排序的数组 建堆,然后不断地删除堆顶元素,就实现了排序。关于堆,参考:数据结构--堆的实现之深入分析 下面的堆排序…

Hessian通信案例(java)
个人博客: 戳我,戳我 前言 由于工作的原因,接触到了hessain,项目需要做hessain和xml之间的报文转换。但是对于hessian是个什么东西一头雾水。于是接下来的时间了解了hessain协议的序列化规则以及hessian协议进行通信的方式。这篇文章是在完成了这个模块…

VDI序曲二十一 APP-V 4.6 SP1服务器端部署
APP-V是微软应用程序虚拟化除RemoteApp以外非常棒的另一种应用程序虚拟化,此应用程序虚拟化是把搭开应用程序消耗的资源放在前端,应用程序虚拟化主要解决的还是软件兼容性问题和保护软件资产问题,同时让用户无需安装就可以绿色使用的手段&…
绝悟之后再超神,腾讯30篇论文入选AI顶会ACL
作者 | 马超责编 | Carol出品| AI科技大本营(ID:rgznai100)封图 | CSDN 付费下载于东方 IC近日,国际计算语言学协会年会ACL在官网(https://www.aclweb.org)公布了2020年度的论文收录名单,其中腾讯共有30篇论文入选&…

mac中用命令行运行mysql
1,安装mysql 在mysql的官方网站下载 mysql 5.5.23 http://www.mysql.com/downloads/mysql/,根据我的机器的配置情况选择了64bit版本。 2,命令行中启动mysql 安装的位置在/usr/local/mysql 于是做了一个别名: $alias mysql/usr/loc…