如何使用查询性能计数器?

Posted

技术标签:

【中文标题】如何使用查询性能计数器?【英文标题】:How to use QueryPerformanceCounter? 【发布时间】:2010-12-16 21:23:49 【问题描述】:

我最近决定需要将 Timer 类从使用毫秒更改为微秒,经过一些研究后,我认为 QueryPerformanceCounter 可能是我最安全的选择。 (Boost::Posix 上的警告说它可能不适用于 Win32 API,这让我有点失望)。但是,我不确定如何实现它。

我正在做的是调用我正在使用的任何GetTicks() esque 函数并将其分配给Timer 的startingTicks 变量。然后要找到经过的时间量,我只需从startingTicks 中减去函数的返回值,当我重置计时器时,我只需再次调用该函数并将startingTicks 分配给它。不幸的是,从我看到的代码来看,它并不像调用 QueryPerformanceCounter() 那样简单,而且我不确定我应该传递什么作为它的参数。

【问题讨论】:

我已将 Ramonster 的代码 sn-ps 放入此处的库中:gist.github.com/1153062 供关注者使用。 我们最近更新了 QueryPerformanceCounter 的文档,并添加了正确使用的附加信息以及常见问题解答。你可以在这里找到更新的文档msdn.microsoft.com/en-us/library/windows/desktop/… 顺便提一下__rdtsc,这是QueryPerformanceCounter使用的。 【参考方案1】:
#include <windows.h>

double PCFreq = 0.0;
__int64 CounterStart = 0;

void StartCounter()

    LARGE_INTEGER li;
    if(!QueryPerformanceFrequency(&li))
    cout << "QueryPerformanceFrequency failed!\n";

    PCFreq = double(li.QuadPart)/1000.0;

    QueryPerformanceCounter(&li);
    CounterStart = li.QuadPart;

double GetCounter()

    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return double(li.QuadPart-CounterStart)/PCFreq;


int main()

    StartCounter();
    Sleep(1000);
    cout << GetCounter() <<"\n";
    return 0;

这个程序应该输出一个接近 1000 的数字(windows sleep 不是那么准确,但应该是 999)。

StartCounter() 函数在CounterStart 变量中记录性能计数器的滴答数。 GetCounter() 函数返回自上次以双精度调用 StartCounter() 以来的毫秒数,因此如果 GetCounter() 返回 0.001,则自调用 StartCounter() 以来已经大约 1 微秒。

如果您想让计时器使用秒数,请更改

PCFreq = double(li.QuadPart)/1000.0;

PCFreq = double(li.QuadPart);

或者如果你想要微秒,那么使用

PCFreq = double(li.QuadPart)/1000000.0;

但实际上它是为了方便,因为它返回一个双精度值。

【讨论】:

究竟,LARGE_INTEGER 是什么? 它是一种 windows 类型,基本上是一个可移植的 64 位整数。它的定义取决于目标系统是否支持 64 位整数。如果系统不支持 64 位整数,那么它被定义为 2 个 32 位整数,一个 HighPart 和一个 LowPart。如果系统确实支持 64 位整数,那么它就是 2 个 32 位整数和一个称为 QuadPart 的 64 位整数之间的联合。 这个答案有严重缺陷。 QueryPerformanceCounter 读取一个内核特定的周期计数器寄存器,如果执行线程已在另一个内核上重新调度,则来自 QueryPerformanceCounter 的两个测量不仅包含经过的时间,而且通常包含两个内核寄存器之间的固定、大且难以精确定位的增量。所以 - 只有当您的流程绑定到特定核心时,这才能可靠地工作。 @TonyD: MSDN documentation 说:On a multiprocessor computer, it should not matter which processor is called. However, you can get different results on different processors due to bugs in the basic input/output system (Bios) or the hardware abstraction layer (HAL). 这段代码没有严重缺陷,但有一些 BIOS 或 HAL。 @TonyD:我只是稍微研究了一下。我在StartCounter 函数中添加了以下调用:old_mask = SetThreadAffinityMask(GetCurrentThread,1);,然后在末尾设置回SetThreadAffinityMask ( GetCurrentThread , old_mask ) ;。我希望这能解决问题。这应该可以防止我的线程被重新安排到除第一个 CPU 内核之外的任何内容。 (这显然只是一个测试环境的解决方案)【参考方案2】:

我使用这些定义:

/** Use to init the clock */
#define TIMER_INIT \
    LARGE_INTEGER frequency; \
    LARGE_INTEGER t1,t2; \
    double elapsedTime; \
    QueryPerformanceFrequency(&frequency);


/** Use to start the performance timer */
#define TIMER_START QueryPerformanceCounter(&t1);

/** Use to stop the performance timer and output the result to the standard stream. Less verbose than \c TIMER_STOP_VERBOSE */
#define TIMER_STOP \
    QueryPerformanceCounter(&t2); \
    elapsedTime=(float)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart; \
    std::wcout<<elapsedTime<<L" sec"<<endl;

用法(括号防止重新定义):

TIMER_INIT


   TIMER_START
   Sleep(1000);
   TIMER_STOP



   TIMER_START
   Sleep(1234);
   TIMER_STOP

使用示例的输出:

1.00003 sec
1.23407 sec

【讨论】:

【参考方案3】:

假设您在 Windows 上(如果是这样,您应该这样标记您的问题!),在 this MSDN page 上,您可以找到一个简单、有用的 HRTimer C++ 类的源代码,该类包装了执行某些操作所需的系统调用非常接近您的要求(很容易向其中添加 GetTicks() 方法,尤其是完全按照您的要求进行操作)。

在非 Windows 平台上,没有 QueryPerformanceCounter 函数,因此该解决方案不能直接移植。但是,如果您确实将其包装在诸如上述HRTimer 之类的类中,则更改该类的实现以使用当前平台确实能够提供的功能会更容易(可能通过Boost 或其他方式!)。

【讨论】:

【参考方案4】:

我会用 NDIS 驱动程序示例来扩展这个问题,以获得时间。众所周知,KeQuerySystemTime(模仿NdisGetCurrentSystemTime)的分辨率低于毫秒级,有些进程如网络数据包或其他IRP可能需要更好的时间戳

例子也很简单:

LONG_INTEGER data, frequency;
LONGLONG diff;
data = KeQueryPerformanceCounter((LARGE_INTEGER *)&frequency)
diff = data.QuadPart / (Frequency.QuadPart/$divisor)

除数为 10^3 或 10^6,具体取决于所需的分辨率。

【讨论】:

以上是关于如何使用查询性能计数器?的主要内容,如果未能解决你的问题,请参考以下文章

WMI 查询 - 2008 性能计数器警报

无论如何,为了提高 SQL 查询的性能,以按标签匹配计数查找具有顺序的行

如何提高 SQL Server 查询的性能以选择具有值的行不在子查询中的一次计数

使用 group by 聚合计数 > 100 万用户 的 Mysql 查询性能变慢

如何访问代码中的 C# 性能计数器?

如何使用性能计数器监控 WCF 服务正常运行时间?