使用增强的计时器测量代码段(Code Section)
介绍
作者:英特尔公司 Paul Work 和 Khang Nguyen
了解如何使用增强的计时器精确测量短时间内的事件。
要测量应用的性能,通常的做法是对代码段进行计时,在代码段中,代码优化前后会出现热点或瓶颈。有很多方法可以测量代码段所花费的时间。最常用的方法是使用秒表对事件计时。系统计时器可用于测量事件,其精确度可达到 10 毫秒。非常适用于可持续 2 秒或更长时间的事件。要测量持续时间小于 10 毫秒的事件的时间,您需要粒度更精细的计时器。由于当今的处理器都在 1GHz 以上的频率上运行,因此您可以使用处理器时钟来计时精确度远远超过 10 毫秒的事件。但是,对于支持英特尔 SpeedStep® 动态节能技术或增强型英特尔 SpeedStep® 动态节能技术的笔记本电脑或系统来说,其处理器频率不会始终保持一致。频率将根据 CPU 利用率发生变化,由此让笔记本电脑在使用电池供电时降低功耗。为了精确测量短时间内的事件,我们需要一个在测量过程中不会发生变化的计时器,而且其精确度必须要比系统计时器高。本文就此类计时器展开讨论。
现有计时器
以下部分将就某些常见的计时器及其局限性进行探讨。
使用秒表
秒表使用起来非常容易且方便。可以快速确定修改后的代码是否比原有代码运行的快。但是,这种计时器的精确度在很大程度上取决于人工的精确度。因此这种计时方法应用来测量持续时间超过 5 秒的事件。它可以用于精确度不是太高的应用。
使用 C 函数计时
为了消除人为错误,可使用 C 函数“time()”。在代码段前后调用函数“time()”,计算其差值可得出执行该代码所需的时间。这种计时器的精确度为 +/-1 秒。它可以对持续时间长达 79 年的事件进行计时。
使用多媒体函数 timeGetTime
对于需要更高精确度的事件来说,可使用多媒体计时器。该函数的名称为 timeGetTime。这种计时器的使用方法与 C 运行时函数相同,在代码段前后调用函数 timeGetTime,获得两次读取之间的差值。这种计时器的精确度可达 +/-10 毫秒,可处理持续时间长达 49 天的事件。
使用处理器时钟
这种计时器非常精确。在采用 3GHz 处理器的系统上,它可以测量持续时间小于 1 纳秒的事件。这种计时器在 3GHz 系统上的精确度为 +/-0.333 纳秒。但是,这种计时器无法通过高级语言直接访问。只能使用汇编指令 Read Time Stamp Counter(RDTSC)才能对其调用。根据时间值的存储方式不同,这种计时器可处理持续时间较长的事件。例如,如果将时间值存储为 32 位值,那么该计时器就可以测量持续时间最多为 1.432 秒的事件。但是,如果返还的时间为 64 位值,那么它就可以对持续时间在 194 年之内的事件进行计时。使用处理器时钟有一个缺点。如,使用英特尔® 奔腾® II 处理器以及以后处理器的笔记本电脑都内置了英特尔 SpeedStep® 动态节能技术。但是英特尔 SpeedStep® 动态节能技术对于笔记本电脑采用电池供电时节约电能非常实用,因为它会改变处理器的频率。如果频率在目标代码运行时发生变化,那么最终的读数就没有意义,因为最初和最终读数不是在时钟频率相同的情况下获得的。在此过程中所出现的时钟计时单元(clock ticks)数量是准确的,但是所耗时间未知。
增强的计时器
该增强的计时器(Etimer)是基于两个 Windows* API 函数,而这两个 API 函数分别是:QueryPerformanceCounter 和 QueryPerformanceFrequency。我们并不清楚微软使用什么频率在给定平台上执行这两个函数。但有一点可以肯定,计时器的频率在计时过程中绝不会发生变化。计时器可能会是芯片组计时器,也可能是电源管理计时器,或其它计时器。Etimer 的创建是为了实现两个目标:首先,它可以作为一种精确度达到纳秒级的高精度计时器,其次,它不受英特尔 SpeedStep® 动态节能技术、增强型英特尔 SpeedStep® 动态节能技术或其它类似技术的限制。操作系统将进行检查,看看系统是否内置了高性能的时钟,如果有的话,系统就没有英特尔 SpeedStep® 动态节能技术此类的能耗节省机制,那么计时器就可以充分利用该时钟的优势,这与处理器时钟最为相似。否则的话,计时器就将使用其它频率一致的时钟,如芯片组、基本输入与输出系统(BIOS)或电源管理计时器。当在您的应用中使用此种时钟时,还有一些因素需要考虑。由于 Etimer 使用系统调用 QueryPerformanceCounter 和 QueryPerformanceFrequency,因此将产生与系统调用相关的开销(overhead)。Etimer 的检查机制也会占用相关的资源,以确保所有的测量均是在相同的处理器上进行的。如果系统开销(overhead)对于您的应用来说过多,您可以通过调用指令 RDTSC,来使用处理器时钟。下面我们就可以使用RDTSC的情形进行探讨。通常在多处理器系统中,Windows 会将时间戳计数器(TSC’s)在所有的处理器上保持同步。TSC 值并不完全相同,但它们之间相差很小。当函数 QueryPerformanceFrequency 的返回值与处理器时钟的相同时,Windows 就将 TSC 保持同步。在这种情况下,您可以安全地使用 TSC,而且不用担心他们来自哪个处理器。因此,您就可以消除所有与该机制相关的系统开销(overhead),该机制会迫使所有的测量在相同的处理器上进行。
使用增强的计时器
以下部分将讨论如何使用 Etimer。
增强计时器的结构
增强的计时器包含两个文件:Etimer.h 和 Etimer.lib。Etimer.h 包含所有的错误信息、数据结构以及 Etimer 库中函数的函数声明。下面部分列出了 Etimer.h 文件的内容。
Etimer.h
下列是 Etimer.h 文件的代码段:
结论
不同的计时器具有不同的用途。Etimer用于需要较高精确度的应用,以及受英特尔 SpeedStep® 动态节能技术这样的节能机制影响的系统。Etimer 在检查多处理器环境中的可用处理器时会占用资源。这可以确保在相同的处理器上获得初始和最终时间。Etimer 在使用系统调用 QueryPerformanceCounter 时也会产生系统开销(overhead)。但是,如果计时器仅是为了检查初始代码与优化过的代码的性能,那么 overhead 的影响就可以互相抵消,因为它在两种情况下都会出现。另外,在重置计时器之前,确保你已经计算了该计时器可以处理的最大值。