使用 std::chrono 库调整应用程序 fps 但出现奇怪的行为
Using std::chrono library to adjust the application fps but getting weird behavior
我使用 std::chrono c++ 库编写了下面的代码,我想做的是
修复应用程序的 FPS
on 60,但我得到 50 FPS
,这肯定不是性能问题
因为我什么都没有计算。但这肯定是无效用法或错误。
TARGET_FPS
宏设置为目标 FPS
我想要获取,然后控制台 window
显示真实的实际 FPS ,以下这些行显示我设置的值 TARGET_FPS
to ,并且每个都与最终的 FPS
.
相关联
TARGET_FPS---->FPS
60----->50
90----->50
100----->100
1000----->100
10000----->100
whatever ----->100
即使我将 TARGET_FPS
定义为 1000000000,我也会得到 100 FPS
,即使我将其定义为 458 或任何大于 100 的值,我也会得到 100 FPS
作为输出。
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#define TARGET_FPS 60// our target FPS
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
const frame_len_type target_frame_len(1); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
void app_logic(){ /** ... All application logic goes here ... **/}
int main() /// our main function !
{
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
std::cout<< fsecond(1) / ( sys_clock::now() - frame_begin) <<std::endl; /// this will show ass the current FPS
}
return 0; /// return to OS
} /// end of code
std::chrono 的时间分辨率取决于系统:
- 在 this answer 另一个问题中,您会找到一个代码片段来确定您的平台的大致时间分辨率。
- 上 windows 7, default timer resolution is 15.6 ms
- 另外,c++标准库不得不依赖的windows API sleep,不保证线程在等待时间过后立即恢复执行:
After the sleep interval has passed, the thread is ready to run. If
you specify 0 milliseconds, the thread will relinquish the remainder
of its time slice but remain ready. Note that a ready thread is not
guaranteed to run immediately. Consequently, the thread may not run
until some time after the sleep interval elapses.
- C++ 标准库没有为
sleep_for
提供更好的保证,无论您使用什么 OS:
30.3.2/7: Effect: Blocks the calling thread for the relative timeout (...)
后果:
- FPS 设置为 60 时,每 16.6 毫秒会有一帧。因此,假设您的
app_logic()
超快,您的线程将至少休眠 15.6 毫秒。如果逻辑需要 1 毫秒来执行,那么您的帧率正好是 60 FPS。
但是,根据 API 文档,如果 [wait time] 大于 1 tick 但小于 2,则等待可以介于 1 和两个 ticks,因此平均睡眠时间将在 15.6 到 31.2 毫秒之间,这反过来意味着您的 FPS 将在 60 到 32 FPS 之间。这就解释了为什么你只能达到 50 FPS。
当你设置FPS为100时,每10ms应该有一帧。这低于定时器精度。可能根本就睡不着。如果没有其他线程准备好 运行,该函数将立即 return,以便您达到最大吞吐量。如果您设置更高的 FPS,您将处于完全相同的情况,因为预期等待时间始终低于计时器精度。因此结果不会改善。
问题已解决:)
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#include <windows.h>
#define TARGET_FPS 500 /// our target fps as a macro
const float target_fps = (float)TARGET_FPS; /// our target fps
float tmp_target_fps = target_fps; /// used to adjust the target fps depending on the actual real fps to reach the real target fps
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
fsecond target_frame_len(1.0f/tmp_target_fps); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
bool enable_fps_oscillation = true;
void app_logic()
{
/** ... All application logic goes here ... **/
}
class HeighResolutionClockKeeper
{
private :
bool using_higher_res_timer;
public :
HeighResolutionClockKeeper() : using_higher_res_timer(false) {}
void QueryHeighResolutionClock()
{
if (timeBeginPeriod(1) != TIMERR_NOCANDO)
{
using_higher_res_timer = true;
}
}
void FreeHeighResolutionClock()
{
if (using_higher_res_timer)
{
timeEndPeriod(1);
}
}
~HeighResolutionClockKeeper()
{
FreeHeighResolutionClock(); /// if exception is thrown , if not this wont cause problems thanks to the flag we put
}
};
int main() /// our main function !
{
HeighResolutionClockKeeper MyHeighResolutionClockKeeper;
MyHeighResolutionClockKeeper.QueryHeighResolutionClock();
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
sys_clock::time_point start_point = sys_clock::now();
float accum_fps = 0.0f;
int frames_count = 0;
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
float fps = fsecond(1) / ( sys_clock::now() - frame_begin) ; /// this will show ass the current FPS
/// obviously we will not be able to hit the exact FPS we want se we need to oscillate around until we
/// get a very close average FPS by time .
if (fps < target_fps) /// our real fps is less than what we want
tmp_target_fps += 0.01; /// lets ask for more !
else if (fps > target_fps ) /// it is more than what we want
tmp_target_fps -=0.01; /// lets ask for less
if(enable_fps_oscillation == true)
{
/// now we will adjust our target frame length for match the new target FPS
target_frame_len = fsecond(1.0f/tmp_target_fps);
/// used to calculate average FPS
accum_fps+=fps;
frames_count++;
/// each 1 second
if( (sys_clock::now()-start_point)>fsecond(1.0f)) /// show average each 1 sec
{
start_point=sys_clock::now();
std::cout<<accum_fps/frames_count<<std::endl; /// it is getting more close each time to our target FPS
}
}
else
{
/// each frame
std::cout<<fps<<std::endl;
}
}
MyHeighResolutionClockKeeper.FreeHeighResolutionClock();
return 0; /// return to OS
} /// end of code
我必须添加 timeBeginPeriod()
and timeEndPeriod()
on windows platform , thanks to this awesome , lost-in-the-wind website http://www.geisswerks.com/ryan/FAQS/timing.html from Ryan Geiss 。
详情:
因为我们实际上无法达到我们想要的确切 fps(略高于或低于 1000 fps,由于 timeXPeriod(1) 而降至 1 fps)因此我使用了一些额外的 dump fps 变量调整我正在寻找的目标 fps,增加它和减少它..,这将使我们能够控制实际应用程序 fps 以平均达到我们的实际目标 fps(您可以使用 'enable_fps_oscillation' 标志启用和禁用它)这解决了 fps = 60 的问题,因为我们无法击中它 (+/-0.5),但如果我们设置 fps = 500,我们就击中了它,我们不需要在它下方和上方振荡
我使用 std::chrono c++ 库编写了下面的代码,我想做的是
修复应用程序的 FPS
on 60,但我得到 50 FPS
,这肯定不是性能问题
因为我什么都没有计算。但这肯定是无效用法或错误。
TARGET_FPS
宏设置为目标 FPS
我想要获取,然后控制台 window
显示真实的实际 FPS ,以下这些行显示我设置的值 TARGET_FPS
to ,并且每个都与最终的 FPS
.
TARGET_FPS---->FPS
60----->50
90----->50
100----->100
1000----->100
10000----->100
whatever ----->100
即使我将 TARGET_FPS
定义为 1000000000,我也会得到 100 FPS
,即使我将其定义为 458 或任何大于 100 的值,我也会得到 100 FPS
作为输出。
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#define TARGET_FPS 60// our target FPS
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
const frame_len_type target_frame_len(1); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
void app_logic(){ /** ... All application logic goes here ... **/}
int main() /// our main function !
{
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
std::cout<< fsecond(1) / ( sys_clock::now() - frame_begin) <<std::endl; /// this will show ass the current FPS
}
return 0; /// return to OS
} /// end of code
std::chrono 的时间分辨率取决于系统:
- 在 this answer 另一个问题中,您会找到一个代码片段来确定您的平台的大致时间分辨率。
- 上 windows 7, default timer resolution is 15.6 ms
- 另外,c++标准库不得不依赖的windows API sleep,不保证线程在等待时间过后立即恢复执行:
After the sleep interval has passed, the thread is ready to run. If you specify 0 milliseconds, the thread will relinquish the remainder of its time slice but remain ready. Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses.
- C++ 标准库没有为
sleep_for
提供更好的保证,无论您使用什么 OS:
30.3.2/7: Effect: Blocks the calling thread for the relative timeout (...)
后果:
- FPS 设置为 60 时,每 16.6 毫秒会有一帧。因此,假设您的
app_logic()
超快,您的线程将至少休眠 15.6 毫秒。如果逻辑需要 1 毫秒来执行,那么您的帧率正好是 60 FPS。 但是,根据 API 文档,如果 [wait time] 大于 1 tick 但小于 2,则等待可以介于 1 和两个 ticks,因此平均睡眠时间将在 15.6 到 31.2 毫秒之间,这反过来意味着您的 FPS 将在 60 到 32 FPS 之间。这就解释了为什么你只能达到 50 FPS。
当你设置FPS为100时,每10ms应该有一帧。这低于定时器精度。可能根本就睡不着。如果没有其他线程准备好 运行,该函数将立即 return,以便您达到最大吞吐量。如果您设置更高的 FPS,您将处于完全相同的情况,因为预期等待时间始终低于计时器精度。因此结果不会改善。
问题已解决:)
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#include <windows.h>
#define TARGET_FPS 500 /// our target fps as a macro
const float target_fps = (float)TARGET_FPS; /// our target fps
float tmp_target_fps = target_fps; /// used to adjust the target fps depending on the actual real fps to reach the real target fps
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
fsecond target_frame_len(1.0f/tmp_target_fps); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
bool enable_fps_oscillation = true;
void app_logic()
{
/** ... All application logic goes here ... **/
}
class HeighResolutionClockKeeper
{
private :
bool using_higher_res_timer;
public :
HeighResolutionClockKeeper() : using_higher_res_timer(false) {}
void QueryHeighResolutionClock()
{
if (timeBeginPeriod(1) != TIMERR_NOCANDO)
{
using_higher_res_timer = true;
}
}
void FreeHeighResolutionClock()
{
if (using_higher_res_timer)
{
timeEndPeriod(1);
}
}
~HeighResolutionClockKeeper()
{
FreeHeighResolutionClock(); /// if exception is thrown , if not this wont cause problems thanks to the flag we put
}
};
int main() /// our main function !
{
HeighResolutionClockKeeper MyHeighResolutionClockKeeper;
MyHeighResolutionClockKeeper.QueryHeighResolutionClock();
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
sys_clock::time_point start_point = sys_clock::now();
float accum_fps = 0.0f;
int frames_count = 0;
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
float fps = fsecond(1) / ( sys_clock::now() - frame_begin) ; /// this will show ass the current FPS
/// obviously we will not be able to hit the exact FPS we want se we need to oscillate around until we
/// get a very close average FPS by time .
if (fps < target_fps) /// our real fps is less than what we want
tmp_target_fps += 0.01; /// lets ask for more !
else if (fps > target_fps ) /// it is more than what we want
tmp_target_fps -=0.01; /// lets ask for less
if(enable_fps_oscillation == true)
{
/// now we will adjust our target frame length for match the new target FPS
target_frame_len = fsecond(1.0f/tmp_target_fps);
/// used to calculate average FPS
accum_fps+=fps;
frames_count++;
/// each 1 second
if( (sys_clock::now()-start_point)>fsecond(1.0f)) /// show average each 1 sec
{
start_point=sys_clock::now();
std::cout<<accum_fps/frames_count<<std::endl; /// it is getting more close each time to our target FPS
}
}
else
{
/// each frame
std::cout<<fps<<std::endl;
}
}
MyHeighResolutionClockKeeper.FreeHeighResolutionClock();
return 0; /// return to OS
} /// end of code
我必须添加 timeBeginPeriod()
and timeEndPeriod()
on windows platform , thanks to this awesome , lost-in-the-wind website http://www.geisswerks.com/ryan/FAQS/timing.html from Ryan Geiss 。
详情:
因为我们实际上无法达到我们想要的确切 fps(略高于或低于 1000 fps,由于 timeXPeriod(1) 而降至 1 fps)因此我使用了一些额外的 dump fps 变量调整我正在寻找的目标 fps,增加它和减少它..,这将使我们能够控制实际应用程序 fps 以平均达到我们的实际目标 fps(您可以使用 'enable_fps_oscillation' 标志启用和禁用它)这解决了 fps = 60 的问题,因为我们无法击中它 (+/-0.5),但如果我们设置 fps = 500,我们就击中了它,我们不需要在它下方和上方振荡