2KB项目,专业的源码交易网站 帮助 收藏 每日签到

使用高级定时器测量代码时间

  • 时间:2019-01-23 18:49 编辑:2KB 来源:2KB.COM 阅读:435
  • 扫一扫,手机访问
  • 分享
摘要: 英文原文:Mea
英文原文:Measure Code Sections Using The Enhanced Timer

本文我们将学习如何使用高级定时器来准确测量短周期事件的处理。

衡量一个应用程序性能的常规做法是关键位置或瓶颈的地方放置计时代码,然后用优化前后的代码执行时间来反应执行效率。现在有很多种方式可以计算代码段的执行时间。最常用的就是在代码停止的地方计时。操作系统的计时器可以达到10ms的精度,对于一些执行时间达到数秒的程序来是,这个精度足够了,但如果我们想测量一些执行时间小于10ms的应用,你就需要粒度更小的计时器。今天主流处理器的频率都已经超过了1GHZ,因此可以利用处理器时钟来进行更精确的测量。但要注意的是,一些便携式电脑或者支持Intel®的SpeedStep技术或增强型SpeedStep技术的电脑,其处理器的时钟频率是不固定的。当便携式电脑使用电池供电的时候,CPU会降低频率以减少电池消耗。要想精确测量短周期事件的处理,我们需要在测量过程中保持时钟频率不变,并且这个计时器的精确度要大于操作系统的计时器,本文就将讨论如何使用这样的高精度计时器。

现存的计时器

下面是目前我们常用的计时器,后面会列出他们的不足之处。

秒表计时

使用秒表非常容易和方便。可以快速的比较代码修改前后执行时间的变化。但是测量精度很大程度上取决于操作的精度。这种测量一般用于执行时间大于5秒的应用,或者不需要太高精度的地方。

使用c语言的time函数

为了避免操作失误,我们可以使用C语言中的time()函数,在程序段的执行前后分别加上time(),并在过程结束的地方计算两个时间的差,就可以得到执行时间。这个计时器的精度大概在1秒左右,最长计时时间可以达79年。

使用多媒体计时函数timeGetTime

对于更高精度的计时可以使用这个函数,timeGetTime(),和上面的C函数一样在代码段开始和结束的地方分别调用。这个函数的精度大概在10毫秒左右,最长计时时间在49天。

使用CPU时钟

CPU时钟非常精确,一个3Ghz的CPU计时精度可以达到0.333纳秒左右,能够在系统中记录1纳秒以下的事件。但是这个计时器,我们还无法使用一些高级编程语言直接访问。我们只能使用汇编语言来访问这个实时时间戳计数器(RDTSC)。计时的长短取决于时间值的存储类型,如果时间值存储为一个32位的值,那么只能计时1.432秒,如果存储为64位的,则可以计时194年。但是使用CPU时钟来计时会有一个缺陷,例如一些便携式电脑的CPU支持INTEL的Speedstep技术,这项技术可以节省电脑的电力消耗。但对于计时来说,如果计时的程序正在运行的时候,CPU时钟频率发生了变化,就会造成前后计时的混乱。CPU时钟在变化之前的计时是精确的,但变化之后的计时就不准了。

增强型高级计时器

增强型高级计时器(ETimer)是基于两个windows系统下的API函数,QueryPerformanceCounter和QueryPerformanceFrequency。因为微软的封闭性,我们无从得知这两个API的实现细节。我们只知道的是这个计时器在计时过程中硬件时钟的频率不会发生变化。这个硬件时钟可能是系统的芯片,也有可能是电源时钟或者其他。这个高级计时器有两个特点,一、可以提供纳秒精度的计时,二、这个计时精度不单纯依赖于CPU时钟,也不会受系统的Speedstep等类似技术的影响。操作系统会检查硬件系统是否有内置的高精度时钟,如果有并且系统的Speedstep等技术未处于激活状态,操作系统就会使用这个时钟,否则系统就会自动使用硬件中其他的时钟,譬如芯片时钟,BIOS时钟,电源时钟等。但是在程序中使用这个计时器需要注意下面的问题,因为这个计时器本身是windows API的系统调用,所以要注意系统调用的开销,另外系统会有一个检查机制来保证所有的计时都是在一个处理器上进行的,这也会产生一定的系统开销。如果这个开销太大,就要考虑采用直接访问RDTSC的方法了。那么我们来看,什么时候可以使用RDTSC。在多CPU的系统中,windows系统会在所有的处理器中自动同步系统的时钟(TSC)。这个同步后的值不会完全相同,但只会相差几个时间片而已。当函数调用返回值时,windows会自动同步TSC值。

QueryPerformanceFrequency返回值和CPU时钟总是相同的,所以你可以安全的使用它,不用考虑这个TSC值是来自哪个处理器,也就可以避免系统的检查机制所产生的调用开销了。

如何使用高级计时器(下文简称:ETimer)

下面是几种使用ETimer的方法:

常规使用

Etimer的使用非常简单。在代码段的开始或者任何你感兴趣的地方调用它,然后在代码段的结束处再调用一次,比较两次的结果就可以得出代码执行的时间。返回值存储在一个Etime_t结构体里。下面是程序里具体调用的步骤:

1、定义一个Etime_t 类型变量,这个结构体是在ETimer.h里定义的。

2、调用EtimeInitialize函数来初始化第一步中定义的变量,然后再调用EtimeFrequency函数,来获取计时器的频率。

3、在你的程序需要计时的开始和结束的地方两次调用Etime

4、调用EtimeDurationInTicksEtimeDurationInSeconds 来获得程序运行的时间

 下面会介绍在单CPU或多CPU系统里运行多线程程序时如何调用计时器。

单CPU下的多线程程序

Etimer 可以用在多线程程序中,但必须为每一个线程单独义定一个 Etime_t变量来存储当前线程的执行时间(Etime_t是在Etimer.h里定义)。因为此变量是在线程内部定义的,所以是线程安全的。

多CPU下的多线程程序

Etimer在多CPU的系统中也可以使用,但计时器的时间从不同的处理器上读取时会有一些细微的差别。所以Etimer的从内部结构设计上避免了这个问题,当在多CPU系统中使用时,它会搜索并使用第一个可用的CPU,这个CPU的ID被存储在Etime_t变量里,最终结束时,也会从相同的CPU上读取时间。如果初始化时从系统里没有发现可用的CPU,Etime函数会返回一个错误。

注意

当在应用程序中使用这个计时器时,请确保:
  • 当要写多线程应用时,要使用 C/C++ 库的多线程版本。
  • 在计时器被复位之前计算它可以处理的最大值。例如,这种增强的定时器使用64位数据类型来处理时间。因此,对于3GHz频率的增强计时器,它能处理长达194年的事件。
  • Etime 函数检查可用的处理器并选择第一个可用的。这将产生开销。然而,如果你想检查原始代码和优化后的代码的性能时,这种开销不会有什么影响。

Etimer的结构

Etimer包含两个文件:Etimer.h和Etimer.lib。头文件Etimer.h里定义了所有的错误信息,数据结构和函数定义,这些函数的具体实现在Etimer库文件里。下面是Etimer.h文件内容:

Etimer.h  文件内容

#include "windows.h"

#define CANNOT_GET_FREQUENCY                      1

#define CANNOT_GET_START_TIME                     2

#define CANNOT_GET_STOP_TIME                      3

#define NO_PROCESSOR_AVAILABLE_TO_GET_START_TIME  4

#define NO_PROCESSOR_AVAILABLE_TO_GET_STOP_TIME   5

#define STOP_TIME_TAKEN_ON_DIFFERENT_PROCESSOR    6



struct Etime_type

{

double		Start,

				Stop,
				Frequency;

DWORD			ProcessorMask;

	BOOL			AlreadyStart;

	int			ErrorFlag;



};



typedef Etime_type Etime_t;


BOOL Etime(Etime_t *);


BOOL EtimeFrequency(Etime_t *);


BOOL EtimeInitialize(Etime_t *);


ULONGLONG EtimeDurationInTicks(Etime_t *);


double EtimeDurationInSeconds (Etime_t *);

错误信息的解释

CANNOT_GET_FREQUENCY:
EtimeFrequency没有正确返回Etimer计时器的频率。

CANNOT_GET_START_TIME:
Etimer不存在(调用开始时间时)

CANNOT_GET_STOP_TIME:
Etimer不存在(调用结束时间时)

NO_PROCESSOR_AVAILABLE_TO_GET_START_TIME:
获取时间的线程不能在任何可用处理器上运行(调用开始时间时)

NO_PROCESSOR_AVAILABLE_TO_GET_STOP_TIME:
获取时间的线程不能在任何可用处理器上运行(调用结束时间时)

STOP_TIME_TAKEN_ON_DIFFERENT_PROCESSOR:

获取线程的线程在运行期间切换到了其他处理器上。

  Etime_t数据结构

struct Etime_type

{

	double		        Start,

				Stop,
				Frequency;

	DWORD		        ProcessorMask;

	BOOL			AlreadyStart;

	int			ErrorFlag;



};


typedef Etime_type Etime_t;
"Start" 和 "Stop"变量会记录代码段的开始和结束时间。"frequency"会存储计时器的频率。"Start" 和 "Stop"的原始格式是CPU时间片,需要根据CPU的频率来转换为秒。 ProcessorMask是一个位向量,每一位表示一个处理器,当前计时使用的处理器对应的数据位会被置1,其余为0。因为多处理器环境下,如果开始和结束测量时间的处理器发生变化,函数会报错,所以就要通过这个数据位来保证所有的操作都是在一个CPU上进行的。“AlreadyStart”是一个布尔值表示系统是否已经开始计时,如果为真,下一次返回的时间就是停止时间。“ErrorFlag”变量里存储错误代码。 Etimer.lib

目前这个库有5个函数,如下:

Etime:
用增强型定计时器捕获当前时间。

EtimeFrequency:
取得增强型计时器的频率。

EtimeInitialize:
重置结构类型 Etime_t 的局部变量,并调用 EtimeFrequency 函数。

EtimeDurationInTicks:
以 tick 为单位计算流逝的时间。

EtimeDurationInSeconds:
以秒为单位计算流逝的时间。 本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部