定时器,线程和编译器错误行为

时间:2012-04-29 15:11:08

标签: c++ windows multithreading timer performancecounter

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

我注意到它给了我奇怪的值,所以我开始打印delta时间并发现它有时会变为0,所以我进入了返回delta时间并进行了一些测试的方法。这是我的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()失败了。

提前致谢!

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

3 个答案:

答案 0 :(得分:3)

几代处理器,QueryPerformanceCounter()将读取CPU的周期计数器(例如rdtsc)。使用此方法,连续读取的滴答数将从不为零。分辨率等于CPU时钟速率,例如3 GHz。

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

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

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

答案 1 :(得分:1)

首先,你的计时器是错误的:它会集中消耗你的CPU。在单核机器上,它将减慢所有系统的速度。如果要创建计时器并定位Windows,可以使用timer functions

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

第三,关于实验结果。似乎如果上下文在两次连续时间测量之间切换一次,则得到0.016s的值,否则,得到0.000001s的值0sQueryPerformanceCounter

正如所说的那样,打印到控制台的操作相对繁重,实际上在启用它时总是会切换上下文。

修改

虽然{{1}}似乎提供了很好的解决方案,但它会让你陷入困境。除非您使用实时操作系统,否则您永远不会获得实际高分辨率计时器。

答案 2 :(得分:1)

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

if(m_deltaTime < 0.000001)
    return 0.0;

它没有任何价值,只是简单地用结果搞定,告诉你实际上没有时间为零。