linux测试代码段运行时间,如何精确测量一段代码的执行时间

原标题:如何精确测量一段代码的执行时间

本文转载自西邮Linux兴趣小组

9829f644c3f91e556e7a33d4f496457f.png

最近在工作中遇到了需要精确测量一段C代码执行时间的需求,这里有三种方案供大家选择:

1:gettimeofday(2)

2:rdtsc/rdtscp

3:clock_gettime(2)

注意:我们讨论环境是运行在Intel x86上的Linux x86_64系统,内核的版本号高于2.6.32。

1

gettimeofday(2)

首先是gettimeofday(2),函数原型如下:

d27bca66666effbb279471936cf4a906.png

原理

从结构体定义上看,这个函数获取到的时间精度是微秒(us,10^-6s)。这个函数获得的系统时间是使用墙上时间xtime和jiffies处理得到的。墙上时间从UTC 1970-01-01 00:00:00开始,由主板电池供电的RTC(实时钟)芯片存储。jiffies是Linux内核启动后的节拍数,Linux内核从2.5版内核开始把频率从100调高到1000,即系统运行频率为1s/1000=1ms(毫秒)。由此可见,仅仅使用这两个来源是无法达到us的精度的。不过在Linux内核中,高精度定时器 hrtimer(High Resolution Timer)模块也会对xtime进行修正的,这个模块甚至支持ns(纳秒,10^-9)的时间精度。

具体应用

在Linux x86_64系统中,gettimeofday的实现采用了“同时映射一块内存到用户态和内核态,数据由内核态维护,用户态拥有读权限”的方式使得该函数调用不需要陷入内核去获取数据,即Linux x86_64位系统中,这个函数的调用成本和普通的用户态函数基本一致(小于1ms)。

总体上来说,微秒级别对于一般的时间获取已经足够,这个函数用在日志输出的时间戳上也很常用,不过对于精度要求很高的场合还是略有些欠缺。

rdtsc/rdtscp

简介

CPU指令rdtsc。这个指令的含义是read tsc寄存器,即time stamp counter寄存器的值。从Pentium处理器开始,Intel的很多80x86微处理器都引入64bit的TSC寄存器,用于时间戳计时器

原理

Readtsc寄存器在每个时钟信号到来时加1。那么这个数值的递增就和CPU的主频相关了,主频为1M Hz的处理器这个寄存器每秒就递增1,000,000次。而rdtsc指令把tsc寄存器的数值读出来,数值的低32位存放在eax寄存器中,高32位存放在edx寄存器中。那么很容易就能用gcc的内联汇编写出来读取这个数值的代码。

代码

a4202cd47d5ac811fb428ecf96a75d10.png

但是这个计时方式在网上很容易找到一些反对的说法,常见的说法有以下几种:

1:从Pentium Pro开始引入的CPU乱序执行使得指令重排序会影响。

2:CPU的频率可能会变化,比如节能模式。

3:无法保证每个CPU核心的TSC寄存器是同步的。

具体解释

1:重排序这个好说,使用cpuid指令保序就行,如果CPU比较新的话直接用rdtscp指令就好,这个已经是保序的指令了。

2:至于频率变化问题,如果是较新的CPU,可以在/proc/cpuinfo文件里看看,如果tsc相关的特性有constant_tsc和nonstop_tsc存在,就不用担心这个了。前者Constant TSC means that the TSC does not change with CPU frequency changes, however it does change on C state transitions,后者The Non-stop TSC has the properties of both Constant and Invariant TSC。

3:不过,多个CPU之间的不同步这里并没有解决。有意思的是,前面的gettimeofday(2)在返回时对xtime和jiffies进行修正时,也有使用TSC寄存器的值。

顺便说一下,我们的场景是单核心测试一段代码的执行时间。多次运行获得运行时间即可,不是持续长期运行的产品代码,所以用rdtscp指令是可以的。

3

clock_gettime(2)

简述

函数"clock_gettime"是基于Linux C语言的时间函数,他可以用于计算精度和纳秒,clock_gettime(2) 其原型如下:

代码

e625b364a0b2a62ec8029f9617fe0827.png

原理

从结构体定义上来看,这是一个ns(纳秒,10^-9)级别精度的时间获取函数。clk_id参数指定获取的时间类型,有以下取值:

1、CLOCK_REALTIME 系统实时时间,从UTC 1970-01-01 00:00:00开始

2、CLOCK_MONOTONIC 从系统启动起开始计时的运行时间,不计算休眠时间

3、CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)类似CLOCK_MONOTONIC,但是基于原始硬件数据,不受NTP时间变动影响

4、CLOCK_PROCESS_CPUTIME_ID 本进程执行到当前代码时系统CPU花费的时间

5、CLOCK_THREAD_CPUTIME_ID 本线程执行到当前代码时系统CPU花费的时间

从参数上看,平时获取时间使用第一个CLOCK_REALTIME参数即可,用这个参数的话有点类似gettimeofday(2),但是精度要高一些(10^-9 vs 10^-6)。事实上当时间类型是CLOCK_PROCESS_CPUTIME_ID或CLOCK_THREAD_CPUTIME_ID时,clock_gettime(2)也有利用rdtsc指令来获取时间。

友情提醒

上面的只是简介,有兴趣的童鞋们可以参考以下的文献:

[1] Linux man pages.

[2] 《Combined Volume Set of Intel® 64 and IA-32 Architectures Software Developer’s Manuals》2B

[2] 《How to Benchmark Code Execution Times on Intel® IA-32 and IA-64 Instruction Set Architectures》Gabriele Paoloni

[3] http://stackoverflow.com/questions/6814792/why-is-clock-gettime-so-erratic返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值