为什么Sleep()会使后续代码减速40ms?

时间:2016-02-28 00:07:16

标签: c++ windows performance sleep

我最初在coderanch.com上询问过这个问题,所以如果你试图在那里帮助我,谢谢,并且不要觉得有必要重复这些努力。但是,coderanch.com主要是一个Java社区,这看起来(经过一些研究后)确实是一个Windows问题,所以我和那里的同事认为这可能是寻找帮助的更合适的地方。

我写了一个简短的程序,要么在Windows性能计数器上旋转直到33ms,要么调用Sleep(33)。前者没有表现出意想不到的效果,但后者似乎(不一致地)使后续处理减慢了大约40ms(或者这对于那个长时间从性能计数器返回的值有一些影响)。在旋转或Sleep()之后,程序调用一个例程runInPlace(),它旋转2ms,计算它查询性能计数器的次数,并返回该数字。

当通过旋转完成最初的33ms延迟时,runInPlace()的迭代次数往往是(在我的Windows 10,XPS-8700上)大约250,000。它有所不同,可能是由于其他系统开销,但它变化平滑约250,000。

现在,当通过调用Sleep()完成初始延迟时,会发生一些奇怪的事情。很多对runInPlace()的调用都会返回一个接近250,000的数字,但是其中相当一部分返回的数字接近50,000。同样,范围变化大约50,000,相当顺利。但是,它显然平均了一个或另一个,几乎没有返回80,000到150,000之间的任何回报。如果我在每次延迟后调用runInPlace()100次,而不是只调用一次,那么在20次调用之后,它永远不会在较小的范围内返回多次迭代。当runInPlace()运行2ms时,这意味着我观察到的行为在40ms后消失。如果我运行runInPlace()运行4ms而不是2ms,它在第10次调用之后永远不会返回较小范围内的迭代次数,因此,再次,行为在40ms后消失(同样如果runInPlace()运行仅1ms;在第40次通话后,行为消失了。

这是我的代码:

#include "stdafx.h"
#include "Windows.h"

int runInPlace(int msDelay)
{
    LARGE_INTEGER t0, t1;
    int n = 0;

    QueryPerformanceCounter(&t0);

    do
    {
            QueryPerformanceCounter(&t1);
            n++;
    } while (t1.QuadPart - t0.QuadPart < msDelay);

    return n;
}

int _tmain(int argc, _TCHAR* argv[])
{
    LARGE_INTEGER t0, t1;
    LARGE_INTEGER frequency;
    int n;

    QueryPerformanceFrequency(&frequency);

    int msDelay = 2 * frequency.QuadPart / 1000;

    int spinDelay = 33 * frequency.QuadPart / 1000;

    for (int i = 0; i < 100; i++)
    {
        if (argc > 1)
            Sleep(33);
        else
        {
            QueryPerformanceCounter(&t0);

            do
            {
                    QueryPerformanceCounter(&t1);
            } while (t1.QuadPart - t0.QuadPart < spinDelay);
        }

        n = runInPlace(msDelay);
        printf("%d \n", n);
    }

    getchar();

    return 0;
}

这里有一些典型的输出,当我使用Sleep()进行延迟时:

56116 248936 53659 34311 233488 54921 47904 45765 31454 55633 55870 55607 32363 219810 211400 216358 274039 244635 152282 151779 43057 37442 251658 53813 56237 259858 252275 251099

这里有一些典型的输出,当我旋转以创建延迟时:

276461 280869 276215 280850 188066 280666 281139 280904 277886 279250 244671 240599 279697 280844 159246 271938 263632 260892 238902 255570 265652 274005 273604 150640 279153 281146 280845 248277

任何人都可以帮我理解这种行为吗? (注意,我在五台计算机上尝试过使用Visual C ++ 2010 Express编译的程序。它只在我拥有的两台最快的机器上显示这种行为。)

2 个答案:

答案 0 :(得分:9)

这听起来像是由于CPU在计算机不忙时运行的时钟速度降低(SpeedStep)。当计算机空闲时(如在睡眠中),时钟速度将下降以降低功耗。在较新的CPU上,这可以是所列时钟速度的35%或更低。一旦计算机再次忙碌,在CPU再次加速之前会有一个小的延迟。

您可以关闭此功能(在BIOS中或通过将电源计划的高级设置中“处理器电源管理”下的“最低处理器状态”设置更改为100%。

答案 1 :(得分:5)

除了 @ 1201ProgramAlarm 之外(可能很好,现代处理器非常喜欢随时都能降频),它也可能是缓存预热问题。

当你要求睡眠一段时间时,调度程序通常会为下一个CPU时间量程调度另一个线程/进程,这意味着缓存(指令缓存,数据缓存,TLB,分支预测数据......)相对于当您的代码重新获得CPU时,您的进程将再次“冷”。