2

我遇到了问题,找不到任何答案,因为我什至不知道要搜索什么。我已经使用 QueryPerformanceCounter 完成了一个计时器类,从我的应用程序中,我启动了第二个线程对象,它有自己的实例化计时器,我只有一个无限循环,从计时器获取增量时间并使用它来输出每个循环迭代的次数第二。

我注意到它给了我奇怪的值,所以我开始打印 delta time 并发现它有时会变为 0,所以我进入了返回 delta time 的方法并进行了一些测试。这是我的 deltaTime() 方法:

    double MyTimer2::deltaTime()
    {
        LARGE_INTEGER timenow;
        QueryPerformanceCounter(&timenow);
        //std::cout << "timenow=" << (double)timenow.QuadPart << "   currentticks=" << (double)m_currentTicks.QuadPart << std::endl;

        double m_deltaTime = (double)(timenow.QuadPart - m_currentTicks.QuadPart) /* 1000.0*/ / (double)m_frequency.QuadPart;

        m_currentTicks = timenow;

        if(m_deltaTime < 0.000001)
            return 0.0;

        return m_deltaTime;
    }

所以,我在“return 0.0;”上放了一个断点;发生的情况是它大部分时间都到达那里,这是不正确的。但是,如果我取消注释打印代码并运行,我将永远不会在断点处停止。所以理论上,我的打印代码使它正常工作,而如果我删除它,事情就会停止正常工作!这怎么可能,为什么会发生,我该如何解决?我试过 _ReadWriteBarrier() 失败。

提前致谢!

编辑:我需要一个用于物理模拟的高分辨率计时器!

4

3 回答 3

3

几代处理器前,QueryPerformanceCounter()会读取 CPU 的周期计数器(例如rdtsc)。使用这种方法,连续读取的滴答数永远不会为零。分辨率等于 CPU 时钟频率,例如 3 GHz。

现代处理器有两个特性使得周期计数器无法用于计时。首先,您有多个内核,每个内核都有自己的周期计数器。线程可以在内核之间迁移,如果您从两个不同的内核读取循环计数器,则差异与经过的时间无关。它甚至可能是负面的。其次,您有基于负载的动态时钟(降低频率以节省电力和超频以提高性能)。英特尔将这些分别称为“SpeedStep”和“Turbo Boost”。当循环率不固定时,就无法从滴答声转换为时间。

因此,QueryPerformanceCounter现在使用一种称为高性能事件计数器 (HPET) 的专用硬件,其分辨率为几兆赫兹。重要的是,无论您拥有多少核心,都只有一个,而且它不会动态改变速度。但是,由于分辨率较低,现在可以在滴答之间读取两次,在这种情况下,您将得到报告为零的经过时间。

在实践中,这不是问题。如果您需要比 HPET 提供的更精确的计时,那么通用计算机不适合您。纳秒范围内的时序会受到中断的严重影响。

于 2012-04-29T16:36:42.283 回答
1

首先,您的计时器是错误的:它会大量消耗您的 CPU。在单核机器上,它会减慢所有系统的速度。如果您想创建一个计时器并以 Windows 为目标,您可以使用计时器函数

然后,您的函数返回的每个非负值deltaTime()都是有效的。虽然您不是在实时操作系统中托管,但每个操作都可能花费任意时间。一次迭代可能需要数十个处理器周期或数十年。没有人保证。

第三,关于实验结果。似乎如果上下文将在两个连续的时间测量之间切换一次,你会得到大约 的值0.016s,如果不是,你会得到0.000001s低于 的值0s

如前所述,打印到控制台是相对繁重的操作,当您启用它时,您实际上总是会切换上下文。

编辑

虽然QueryPerformanceCounter似乎提供了很好的分辨率,但它会让你陷入困境。除非您在实时操作系统中工作,否则您永远不会获得真正的高分辨率计时器。

于 2012-04-29T15:57:32.360 回答
1

这个区块的目的可能是什么?

if(m_deltaTime < 0.000001)
    return 0.0;

它没有任何价值,它只是与结果挂钩,告诉你时间为零,而实际上并非如此。

于 2012-04-29T16:02:02.720 回答