65.9K
CodeProject 正在变化。 阅读更多。
Home

CCPUTicker v1.22 - 精确计时

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.44/5 (9投票s)

2000 年 3 月 4 日

viewsIcon

157355

downloadIcon

2080

适用于 Pentium 或更高 CPU 的超高精度 MFC 计时类。

  • 下载源文件 - 16 Kb
  • 引言

    欢迎使用 CCPUTicker,这是一款适用于 Pentium 或更高 CPU 的超高精度 MFC 计时类,可用于分辨率计时。


    特点
    历史
    API 参考
    用法
    联系作者


    特点

    此类实现了一个 MFC 封装类,用于 Pentium 特定的时间戳计数器,该计数器可以使用 RDTSC 汇编语言指令进行访问。此计数器的分辨率以 PCLKS(处理器时钟)为单位,因此如果您拥有一个 200 MHz 的 CPU,此类将提供 200 MHz 的频率。返回的值是一个 64 位整数,因此假设您的 CPU 运行频率为 200 MHz,则该值大约需要 3000 年才能回绕。由于该值也从 0 开始计数,因此返回的值是自计算机开机以来经过的 CPU 滴答数,即“开机”时间。

    由于计时器是 CPU 硬件的一部分,因此不受处理器活动和工作负载的影响。此类仅在 Intel CPU 上进行了测试。欢迎提供关于其在其他 CPU 类型上行为的反馈。此类也可以在 Windows NT 上毫无问题地使用。

    此类本身最初由 J.M.McGuiness 开发,并由两位作者共同开发。

    源 zip 文件包含 CCPUTicker 源代码,还包含一个简单的基于 MFC 消息框的演示应用程序,该应用程序将计时 SDK 调用 Sleep(1000) 的精度,并报告您的计算机已“开机”多长时间。


    历史

    V1.0(1996 年 3 月 26 日)

    V1.1(1997 年 7 月 16 日)

    • 支持在 Windows NT 上运行。
    • 现在使用内置的 64 位数据类型 __int64
    • 得益于上述改进,诊断信息得到了增强。
    • 使用静态变量以提高效率。
    • 添加了一个可以将 CPU 滴答数转换为秒的函数。
    • 改进了对 MFC 编码风格和标准的遵循。

    V1.2(1999 年 1 月 14 日)

    • 修复了 David Green-Seed 发现的一个 bug,他在该 bug 中,当代码在 Pentium II 上启用优化编译时,会遇到访问冲突。问题在于(在 PII 上)RDTSC 指令触及的寄存器比预期要多。现在通过在调用 RDTSC 周围保存和恢复 EAX 和 EBX 寄存器来修复了此问题。
    • 为该类提供了 HTML 文档(本文档)。
    • 现在标配提供了一个 Visual C++ 5.0 工作区文件。
    • 代码现在可以在警告级别 4 下干净地编译。
    • 对 const 函数、参数等进行了通用代码清理。
    • 示例程序现在还报告计算机的“开机”时间。

    V1.21(1999 年 1 月 14 日)

    • 未包含任何代码更改。仅更新了帮助文件,其中包含一些可能引起 CCPUTicker 用户兴趣的开发说明。

    1999 年 1 月 21 日

    • 未包含任何代码更改。仅更新了本文档,说明在 SMP 机器上的使用情况。

    1999 年 1 月 27 日

    • 未包含任何代码更改。仅更新了本文档,说明在 SMP 机器上的使用情况。

    v1.22(1999 年 12 月 3 日)

    • 修复了在 VC 6 中以发布模式编译时出现的一个问题。


    API 参考

    API 由 CCPUTicker 类的以下公共方法组成

    CCPUTicker()
    operator=()
    Measure()
    GetTickCountAsSeconds()
    GetTickCount()
    GetCPUFrequency()
    IsAvailable()
    AssertValid()
    Dump()


    CCPUTicker::CCPUTicker

    CCPUTicker();
    CCPUTicker(const CCPUTicker &ticker);

    参数

    • ticker -- CCPUTicker 的另一个实例

    备注
    类的标准构造函数和复制构造函数,它们仅初始化一些内部变量。


    CCPUTicker::operator=

    CCPUTicker& operator=(const CCPUTicker &ticker);

    参数

    • ticker -- CCPUTicker 的另一个实例

    返回值
    C++ operator= 函数的标准 this 指针引用。

    备注
    类的标准 operator=。


    CCPUTicker::Measure

    void CCPUTicker::Measure();

    备注
    调用此函数可将 RDTSC 计数器的当前值检索到此 CCPUTicker 实例中。


    CCPUTicker::GetTickCountAsSeconds

    double GetTickCountAsSeconds() const;

    返回值
    以秒为单位的当前 RDTSC 计数器值。

    备注
    调用此函数可从该实例检索以秒为单位存储的 RDTSC 计数器值。秒数是计算机的“开机”时间。由于计数器以时钟滴答数存储,因此当您的任何代码首次调用此函数时,处理器时钟频率将使用内部计时例程进行估算。请注意,这可能会导致当前进程挂起长达 20 秒。有关此函数的开发注意事项,请参阅下面的用法部分。


    CCPUTicker::GetTickCount

    __int64 CCPUTicker::GetTickCount() const;

    返回值
    以时钟滴答数为单位的当前 RDTSC 计数器值。

    备注
    调用此函数可从该实例检索以时钟滴答数为单位存储的 RDTSC 计数器值。此函数是对类在其上次调用 Measure 方法时获得的值的简单访问器。


    CCPUTicker::GetCPUFrequency

    static BOOL GetCPUFrequency(double& frequency, double& target_ave_dev, unsigned long interval = 1000, unsigned int max_loops = 20) const;

    参数

    • frequency -- 成功返回后,将包含处理器时钟频率(赫兹)。
    • target_ave_dev -- 成功返回后,将包含处理器时钟频率的估计平均偏差(赫兹)。
    • interval -- 每个计时循环的间隔(毫秒)。
    • max_loops -- 用于估算处理器时钟频率的最大计时循环次数。

    返回值
    如果 CPU 频率成功返回,则为 TRUE,否则为 FALSE。如果发生这种情况,请使用 GetLastError() 确定原因。

    备注
    此函数将处理器时钟频率计算到由目标平均偏差决定的特定精度。请注意,对于 90 MHz 的平均频率,结果的最坏平均偏差小于 5 MHz。因此,目标平均偏差仅在您需要更精确的结果时提供,它不会让您获得更差的结果。(单位是 Hz)。平均偏差比其近亲标准偏差更好、更稳健的度量。两者确定的项目本质上是相似的。有关更多详细信息,请参阅“数值食谱”(W.Press 等人)。此函数默认最多运行 20 秒,然后放弃改进平均偏差,实际实现的平均偏差将替换提供的目标值。使用“max_loops”进行更改。要提高函数收敛的值,请增加“interval”(单位是 ms,默认值为 1000ms)。有关此函数的开发注意事项,请参阅下面的用法部分。


    CCPUTicker::IsAvailable

    BOOL IsAvailable() const;

    返回值
    如果此计算机具有用于计时的“RDTSC”指令,则为 TRUE,否则为 FALSE。


    CCPUTicker::AssertValid

    virtual void AssertValid() const;

    备注
    标准 MFC 诊断函数。


    CCPUTicker::Dump

    virtual void Dump(CDumpContext& dc) const;

    备注
    标准 MFC 诊断函数。



    用法

    示例应用程序是一个简单的基于 MFC 消息框的演示,它将计时 SDK 调用 Sleep(1000) 的精度,并报告您的计算机已“开机”多长时间。

    要在您的项目中使用的 CCPUTicker,只需在测试应用程序中包含 cputicker.cpp 到您的应用程序中,并在您想使用该类的任何文件中 #include "cputicker.h"

    在开发使用 CCPUTicker 的代码时,应牢记以下几点:

    在 SMP 机器(对称多处理机器)上使用,即具有 2 个或更多 CPU 的 NT
    考虑以下场景:我们打算通过两次读取 RDTSC 并使用差值/频率来获得某个时间(真实时间)的度量。假设第一次读取是从 CPU0 读取的,第二次读取是从 CPU1 读取的(进程不一定始终在同一 CPU 上运行)。由于 CPU 的 RDTSC 指令是独立的,一个 CPU 上的 RDTSC 与另一个 CPU 上的 RDTSC 完全无关,因此读数将完全无关!您应该在应用程序级别解决此问题。可以通过调用 GetSystemInfo SDK 函数并检查返回结构中的 dwNumberOfProcessors 来检测 SMP 机器。您还应该查看 SetProcessAffinityMask 函数,以确保您的代码始终只在一个 CPU 上运行。

    关于 APM、ACPI、CPU 过热
    在许多机器上,CPU 的时钟速度可以被这些技术减慢(然后再次提高)。因此,从时钟周期计算实际时间可能无效。(假设在第一次和第二次读取之间更改了时钟,或者假设时钟频率在一个速度下计算,然后又发生了更改……)。请注意,调用 GetCPUFrequency 和 GetTickCountAsSeconds 取决于计时器期间 CPU 频率的不变性。如果 CPU 频率随时间变化,则报告为秒的内容可能不准确。如果您只想检索计算机的开机时间,则应使用 GetTickCount SDK 函数。


    联系作者

    PJ Naughter
    电子邮件: pjn@indigo..ie
    Web:
    © . All rights reserved.