本文我们将学习如何使用高级定时器来准确测量短周期事件的处理。
衡量一个应用程序性能的常规做法是关键位置或瓶颈的地方放置计时代码,然后用优化前后的代码执行时间来反应执行效率。现在有很多种方式可以计算代码段的执行时间。最常用的就是在代码停止的地方计时。操作系统的计时器可以达到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、调用EtimeDurationInTicks或EtimeDurationInSeconds 来获得程序运行的时间
下面会介绍在单CPU或多CPU系统里运行多线程程序时如何调用计时器。
单CPU下的多线程程序
Etimer 可以用在多线程程序中,但必须为每一个线程单独义定一个 Etime_t变量来存储当前线程的执行时间(Etime_t是在Etimer.h里定义)。因为此变量是在线程内部定义的,所以是线程安全的。
多CPU下的多线程程序
Etimer在多CPU的系统中也可以使用,但计时器的时间从不同的处理器上读取时会有一些细微的差别。所以Etimer的从内部结构设计上避免了这个问题,当在多CPU系统中使用时,它会搜索并使用第一个可用的CPU,这个CPU的ID被存储在Etime_t变量里,最终结束时,也会从相同的CPU上读取时间。如果初始化时从系统里没有发现可用的CPU,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
2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务