统一规划程序执行率[Windows C ++]

时间:2012-05-14 23:14:00

标签: c++ windows sleep

首先,我发现了很多关于这个主题的信息,但遗憾的是没有解决这个问题的解决方案。

我只是试图规范我的C ++程序,以每秒60次迭代运行。我已尝试从GetClockTicks()到GetLocalTime()的所有内容来帮助进行监管,但每次我在Windows Server 2008计算机上运行程序时,它运行速度都比我本地计算机慢,我不知道为什么!

据我所知,基于“时钟”的函数调用会在执行时返回CPU时间,所以我去GetLocalTime,然后尝试区分开始时间和停止时间,然后调用Sleep((FPS / 1000) - millisecondExecutionTime)< / p>

我的本​​地计算机比服务器CPU快得多,所以很明显人们认为它已经脱离了CPU滴答声,但这并不能解释为什么GetLocalTime不起作用。我一直把这个方法从http://www.lazyfoo.net/SDL_tutorials/lesson14/index.php改为get_ticks(),所有时间都返回我在网上找到的函数。

例如,请使用以下代码:

#include <Windows.h>
#include <time.h>
#include <string>
#include <iostream>

using namespace std;

int main() {
    int tFps = 60;
    int counter = 0;

    SYSTEMTIME gStart, gEnd, start_time, end_time;

    GetLocalTime( &gStart );

    bool done = false;
    while(!done) {
        GetLocalTime( &start_time );

        Sleep(10);
        counter++;

        GetLocalTime( &end_time );

        int startTimeMilli = (start_time.wSecond * 1000 + start_time.wMilliseconds);
        int endTimeMilli = (end_time.wSecond * 1000 + end_time.wMilliseconds);

        int time_to_sleep = (1000 / tFps) - (endTimeMilli - startTimeMilli);


        if (counter > 240)
            done = true;

        if (time_to_sleep > 0)
            Sleep(time_to_sleep);
    }

    GetLocalTime( &gEnd );

    cout << "Total Time: " << (gEnd.wSecond*1000 + gEnd.wMilliseconds) - (gStart.wSecond*1000 + gStart.wMilliseconds) << endl;
    cin.get();
}

对于此代码段,在我的计算机上运行(3.06 GHz)我的总时间(ms)为3856,而在我的服务器(2.53 GHz)上,我得到6256.所以它可能是处理器的速度,尽管比率为2.53 / 3.06仅为.826797386而3856/6271为.614893956。

我无法分辨睡眠功能是否正在做一些与预期完全不同的事情,虽然我不明白它为什么会这样,或者它是否是我获取时间的方法(即使它应该是世界时间(ms) )不是时钟周期时间。非常感谢任何帮助,谢谢。

5 个答案:

答案 0 :(得分:3)

首先,Sleep的默认分辨率是计算机的配额长度 - 通常为10毫秒或15毫秒,具体取决于Windows版本。要获得1ms的分辨率,你必须发出一个timeBeginPeriod(1),它将计时器硬件重新编程为每毫秒一次(大致)触发。

答案 1 :(得分:1)

在你的主循环中你可以

int main() 
{
    // Timers
    LONGLONG curTime = NULL;
    LONGLONG nextTime = NULL;

    Timers::GameClock::GetInstance()->GetTime(&nextTime);
    while (true) {    
        Timers::GameClock::GetInstance()->GetTime(&curTime);
        if ( curTime > nextTime  && loops <= MAX_FRAMESKIP ) { 
            nextTime += Timers::GameClock::GetInstance()->timeCount;

            // Business logic goes here and occurr based on the specified framerate
        }
    }
}

使用这个时间库

include "stdafx.h"

LONGLONG cacheTime;

Timers::SWGameClock* Timers::SWGameClock::pInstance = NULL;

Timers::SWGameClock* Timers::SWGameClock::GetInstance ( ) { 
    if (pInstance == NULL) { 
        pInstance = new SWGameClock();
    }
    return pInstance;
}


Timers::SWGameClock::SWGameClock(void) {
    this->Initialize ( );
}

void Timers::SWGameClock::GetTime ( LONGLONG * t ) { 
    // Use timeGetTime() if queryperformancecounter is not supported 
    if (!QueryPerformanceCounter( (LARGE_INTEGER *) t)) { 
        *t = timeGetTime();
    }

    cacheTime = *t;
}

LONGLONG Timers::SWGameClock::GetTimeElapsed ( void ) { 
    LONGLONG t; 

    // Use timeGetTime() if queryperformancecounter is not supported
    if (!QueryPerformanceCounter( (LARGE_INTEGER *) &t )) { 
        t = timeGetTime();
    }

    return (t - cacheTime);
}

void Timers::SWGameClock::Initialize ( void ) { 
    if ( !QueryPerformanceFrequency((LARGE_INTEGER *) &this->frequency) ) { 
        this->frequency = 1000; // 1000ms to one second 
    }
    this->timeCount = DWORD(this->frequency / TICKS_PER_SECOND);
}

Timers::SWGameClock::~SWGameClock(void)
{
}

包含以下内容的头文件:

// Required for rendering stuff on time
#pragma once
#define TICKS_PER_SECOND 60
#define MAX_FRAMESKIP 5

namespace Timers { 
    class SWGameClock
    {
    public:
        static SWGameClock* GetInstance();
        void Initialize ( void );
        DWORD timeCount;

        void GetTime ( LONGLONG* t );
        LONGLONG GetTimeElapsed ( void );
        LONGLONG frequency; 

        ~SWGameClock(void);
    protected:
        SWGameClock(void);

    private:
        static SWGameClock* pInstance;
    }; // SWGameClock
} // Timers

这将确保您的代码以60FPS(或您输入的任何内容)运行,但您可以转储MAX_FRAMESKIP,因为在此示例中并未真正实现!

答案 2 :(得分:0)

您可以尝试使用WinMain函数并使用SetTimer函数和常规消息循环(您也可以利用GetMessage( ... )的过滤机制)来测试带有请求时间的WM_TIMER消息以及当您的计数器达到限制时,请执行PostQuitMessage(0)以终止消息循环。

答案 3 :(得分:0)

对于快速的占空比,您可以使用高精度定时器(如QueryPerformanceTimer)和忙等待循环。

如果你的占空比要低得多,但仍然需要精确度,那么你可以在一段时间内睡觉,然后在忙碌等待循环中吃掉剩余的时间。

另一种选择是使用类似DirectX的东西将自己同步到VSync中断(几乎总是60 Hz)。如果您正在编写游戏或/ v演示文稿,这可能会很有意义。

Windows不是一个实时操作系统,因此永远不会有一个完美的方式来做这样的事情,因为无法保证您的线程将被安排在您需要时准确运行。

请注意,在remarks for Sleep中,实际的时间长度至少为一个“tick”,并且整个“tick”可能比线程计划再次运行之前请求的延迟时间长(然后我们必须假设线程被安排)。 “tick”可能会有很大差异,具体取决于硬件和Windows的版本。它通常在10-15毫秒的范围内,我已经看到它像19毫秒一样糟糕。对于60 Hz,每次迭代需要16.666 ms,因此这显然不够精确,无法满足您的需求。

答案 4 :(得分:0)

基于每帧渲染之间经过的时间渲染(迭代)怎么样?考虑创建void render(double timePassed)函数并根据timePassed参数进行渲染,而不是让程序进入休眠状态。

想象一下,例如,你想渲染一个球落下或弹跳。你会知道它的速度,加速度和你需要的所有其他物理。根据{{​​1}}和所有其他物理参数(速度,加速度等)计算球的位置。

或者,如果您愿意,如果时间过去是一个较小的值而不是puttin程序进入睡眠状态,您可以跳过timePassed函数执行。