如何使用QueryPerformanceCounter?

Ano*_*ous 94 c++ windows timer

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

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

Ram*_*ter 155

#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;
}
Run Code Online (Sandbox Code Playgroud)

这个程序应该输出接近1000的数字(窗口睡眠不准确,但它应该像999).

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

如果你想让计时器使用秒而不是更改

PCFreq = double(li.QuadPart)/1000.0;
Run Code Online (Sandbox Code Playgroud)

PCFreq = double(li.QuadPart);
Run Code Online (Sandbox Code Playgroud)

或者如果你想要微秒然后使用

PCFreq = double(li.QuadPart)/1000000.0;
Run Code Online (Sandbox Code Playgroud)

但实际上它是关于方便,因为它返回一个双倍.

  • @TonyD:[MSDN文档](http://msdn.microsoft.com/en-us/library/ms644904%28VS.85%29.aspx)说:`在多处理器计算机上,调用哪个处理器无关紧要.但是,由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的结果.这个代码没有严重缺陷,但有些BIOS或HAL. (13认同)
  • 这个答案存在严重缺陷.QueryPerformanceCounter读取核心特定循环计数器寄存器,如果执行线程已在另一个核心上重新调度,则QueryPerformanceCounter的两个测量不仅包含经过的时间,而且通常是两个核心寄存器之间固定的,大的且难以精确定位的增量.所以 - 如果您的流程绑定到特定核心,这只能可靠地运行. (7认同)
  • 究竟什么是LARGE_INTEGER? (4认同)
  • 它是一种Windows类型,基本上是一个可移植的64位整数.它的定义取决于目标系统是否支持64位整数.如果系统不支持64位整数,那么它被定义为2 32位整数,一个HighPart和一个LowPart.如果系统支持64位整数,则它是2 32位整数和64位整数之间的联合,称为QuadPart. (4认同)
  • @TonyD:我只是稍微研究了一下.我将以下调用添加到`StartCounter`函数中:`old_mask = SetThreadAffinityMask(GetCurrentThread,1);`然后将其设置回`SetThreadAffinityMask(GetCurrentThread,old_mask);`.我希望能做到这一点.这应该可以防止我的线程被重新安排到除了第一个CPU核心之外的任何东西.(这显然只是测试环境的解决方案) (3认同)
  • 我建议指出性能计数器频率被视为常数.实际上,为频率获得的值偏离了对QueryPerformanceFrequency的调用返回的`const`.[了解更多......](http://stackoverflow.com/a/11537483/1504523) (2认同)

小智 18

我使用这些定义:

/** 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;
Run Code Online (Sandbox Code Playgroud)

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

TIMER_INIT

{
   TIMER_START
   Sleep(1000);
   TIMER_STOP
}

{
   TIMER_START
   Sleep(1234);
   TIMER_STOP
}
Run Code Online (Sandbox Code Playgroud)

使用示例的输出:

1.00003 sec
1.23407 sec
Run Code Online (Sandbox Code Playgroud)