如何在Windows上使用快速高分辨率计时器?

如何在Windows上使用快速高分辨率计时器?,windows,winapi,timing,Windows,Winapi,Timing,我正在编写一个探查器,它在函数进入或退出时查询计时器。因此,它有可能每秒被查询数千次 起初,我使用了QueryPerformanceCounter,尽管它的分辨率很高,但结果却是相当慢。根据问题,当我在剖析器中使用QPC时,我也得到了一个明显的减速,但可能没有那么糟糕的1-2ms图。如果我用GetTickCount替换它,我没有注意到任何减速,但该函数对于剖析来说是不准确的 提到的问题提到亲和力面具。我尝试使用SetProcessAffinityMask(GetCurrentProcess(),

我正在编写一个探查器,它在函数进入或退出时查询计时器。因此,它有可能每秒被查询数千次

起初,我使用了QueryPerformanceCounter,尽管它的分辨率很高,但结果却是相当慢。根据问题,当我在剖析器中使用QPC时,我也得到了一个明显的减速,但可能没有那么糟糕的1-2ms图。如果我用GetTickCount替换它,我没有注意到任何减速,但该函数对于剖析来说是不准确的

提到的问题提到亲和力面具。我尝试使用
SetProcessAffinityMask(GetCurrentProcess(),1)
绑定它,但它根本没有提高性能


我不知道这是否重要,但到目前为止,我在Linux主机上运行VirtualBox的Windows上测试了它。可能是问题吗?

据我所知,Windows上分辨率最高的计时器是通过
winmm.dll

以下是我为满足自己的性能测试需求而开设的一门课程——让我们试一试:

public class HighResTimer
{
    private delegate void TimerEventHandler(int id, int msg, IntPtr user, int dw1, int dw2);

    private const int TIME_PERIODIC = 1;
    private const int EVENT_TYPE = TIME_PERIODIC;

    [System.Runtime.InteropServices.DllImport("winmm.dll")]
    private static extern int timeSetEvent( int delay, int resolution, TimerEventHandler handler, IntPtr user, int eventType);  
    [System.Runtime.InteropServices.DllImport("winmm.dll")]
    private static extern int timeKillEvent(int id);
    [System.Runtime.InteropServices.DllImport("winmm.dll")]
    private static extern int timeBeginPeriod(int msec);
    [System.Runtime.InteropServices.DllImport("winmm.dll")]
    private static extern int timeEndPeriod(int msec); 

    private int _timerId;
    private TimerEventHandler _handler = delegate {};

    public event EventHandler OnTick;

    public HighResTimer(int delayInMs)
    {
        timeBeginPeriod(1);
        _handler = new TimerEventHandler(timerElapsed);
        _timerId = timeSetEvent(delayInMs, 0, _handler, IntPtr.Zero, EVENT_TYPE);
    }

    public void Stop()
    {
        int res = timeKillEvent(_timerId);
        timeEndPeriod(1);
        _timerId = 0;
    }

    private void timerElapsed(int id, int msg, IntPtr user, int dw1, int dw2)
    {
        OnTick(this, new EventArgs());
    }   
}

最终直接使用了RDTSC指令。所以我在GCC中为它编写了一个包装器:

static inline unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
静态内联无符号长rdtsc(void)
{
未签名的hi,lo;
asm易失性(“rdtsc”:“=a”(lo),“=d”(hi));

return((unsigned long-long)lo)|((unsigned long-long)hi)请记住,多媒体计时器在其自己的线程中运行,并且有
已被替换为。线程池计时器、计时器队列计时器和可等待计时器都具有与多媒体计时器相同的毫秒分辨率。正如前面的评论所述,您应该使用这些计时器中的一个,而不是多媒体计时器,多媒体计时器已被认为过时多年。更重要的是,您的计时器在bes上提供了t 1毫秒分辨率。
QueryPerformanceCounter
可以提供接近微秒的分辨率。当然,它不是一个周期计时器,但OP使用它来计算经过的时间,而不是定期执行代码。知道
winmm
的弃用状态,不知道替代方案-cheers@JimMischel@RemyLebeau看起来很有趣哦,是的,对于使用低级功能的代码来说,虚拟化可能是一个问题,例如…使用CPU寄存器的计时器;-)RDTSC指令在现代处理器上再次可用。QueryThreadCycleTime()也使用了它。请记住,转换“周期”到时间不是直截了当的。@HansPassant说QueryThreadCycleTime可以从Vista和更高版本获得。出于性能原因,我的VM仍然是XP,在这里不可用。因此我无法测试它。我最终直接调用了rdtsc指令,它起了作用。我不需要将周期转换为时间。如果我能找到燃烧的函数,这就足够了循环次数最多。将虚拟机作为探查器的目标有点像在地下室建造潜艇。你可能会完成它,但无论如何都无法让它上楼发挥作用。