使用MinGW C编程语言的Windows QueryPerformanceCounter

3
我将尝试通过调用QueryPerformanceCounter(QPC)来测量串行协议握手的响应时间。我的目标系统是一台装有Win7操作系统的Asrock D1800b Intel双核计算机,并使用MinGw的最新版本进行编译(6.3.0-1)。
为了测试QPC,我有以下代码:
#include <windows.h>
#include <stdio.h>

void main() 
{
    //performance timers
    LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
    LARGE_INTEGER Frequency;

    QueryPerformanceFrequency(&Frequency);
    for (int i = 0 ; i < 10 ; i++) 
    {
        QueryPerformanceCounter(&StartingTime);
        Sleep(10); //ms
        QueryPerformanceCounter(&EndingTime);
        ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
        ElapsedMicroseconds.QuadPart *= 1000000;                //first scale up counts
        ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;     //then convert to us
        printf("elapsed=%li\n", ElapsedMicroseconds.QuadPart);
    }

}

当在目标系统上运行时,我得到如下结果:(没有其他负载)
经过时间=5341 经过时间=14086 经过时间=13818 经过时间=14322 经过时间=15305 经过时间=8867 经过时间=12162 经过时间=14225 经过时间=13333 经过时间=14751
但是当在开发系统(Win10,Intel i5)上运行时,我得到了更加一致的结果:
经过时间=11326 经过时间=11556 经过时间=12630 经过时间=11583 经过时间=11749 经过时间=12644 经过时间=12562 经过时间=11690 经过时间=11726 经过时间=11664
以上两个结果都是大量运行的示例。
预期结果应该在10000微秒以上。
那么,有什么想法可以解释目标系统上发生了什么?

1
目标系统是否正在经历一些持续且相当大的负载变化? - CristiFati
尝试使用50毫秒。10毫秒接近于睡眠方法不可靠的极限。好吧,我猜睡眠在这里只是为了举例说明,对吧? - Jean-François Fabre
这里的关键是Windows不是实时操作系统,也没有任何关于成为实时操作系统的假设。特别地,Sleep()无法保证确切的延迟执行时间。您会尽力而为,但仅此而已。QueryPerformanceCounter()相当准确,但如果我可以打个比方,你现在做的就像是用千分尺来测量用斧头手工切割的木材大小一样。 - dgnuff
我明白Windows不是一个RTOS,但我认为在Sleep上的尽力而为会导致更长的等待时间,而不是更短的。 - MartinBuc
此示例的目标是验证 QPC 的频率参数,正如本文中所提到的警告: https://learn.microsoft.com/en-us/windows/desktop/sysinfo/acquiring-high-resolution-time-stamps - MartinBuc
显示剩余3条评论
1个回答

1

您可能需要增加时钟频率,因为默认情况下可能是64hz(每个时钟周期15.625毫秒),以便使Sleep(10)按预期工作。您可以将其增加到1000hz(每个时钟周期1毫秒)。这将增加中断开销,但结果应该更加一致。示例代码片段:

    timeBeginPeriod(1);                     /* set ticker to 1000 hz */
    Sleep(128);                             /* wait for it to settle */
    /* ... benchmark code */
    timeEndPeriod(1);                       /* restore ticker to default */

请注意,尽管Windows不是实时操作系统,但在Windows中可以使线程以固定频率运行而没有任何漂移,并且在可能的情况下使用Sleep(1)来避免在固定频率线程中占用CPU时间。如果有竞争线程,则将线程运行在稍高的优先级上会有所帮助。这通常被需要以固定频率运行的“物理引擎”线程的游戏所使用。示例代码包含在回答这个旧问题的链接中: 如何基于固定循环频率正确协调线程? 使用固定频率为100hz的示例程序输出的示例结果,运行了100次。每个步骤都有一些变化,但总体上没有漂移。
 100 deltas in ms:

  9.99973023  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00676775  10.00002346 
 10.00002346  10.00002346  10.00794067  10.00002346 
  9.99973023  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00618129  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346   9.99973023  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00090315  10.00148961  10.00148961  10.00090315 
 10.00002346  10.00178284  10.00002346  10.00002346 
 10.00295576  10.00002346  10.00882036  10.00500837 
 10.00002346  10.00559483  10.00559483  10.00647452 
 10.00588806  10.00735421  10.00676775  10.00764744 
 10.00002346  10.00002346  10.00823390  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346   9.99973023  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346   9.99973023 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346   9.99973023 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 
 10.00002346  10.00002346  10.00002346  10.00002346 

elapsed time in ms:

1000.000000000000

我理解你的观点,但是这个线程与睡眠准确性关系不大,而是为了理解它与QPC的关系。 到目前为止,我所知道的最好的答案是,Windows可能选择不为Sleep调用设置较低的阈值,只是尽力完成时间。 非常感谢您的帮助。 - MartinBuc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接