I\'m trying to limit the frames per second in a loop that is performing intersection checking, using C++ with chrono and thread.
Here is my code:
std::ch
If you think about how your code works, you'll find out that it works exactly how you wrote it. Delta oscillates because of a logical mistake in the code.
This is what happens:
delta == 0
.200
, you code sleeps 200 - delta(0) == 200
ms.200
(because you've measured that sleep time as well as an actual work) and you sleep 200 - delta(200) == 0
ms.To fix the problem you need to not measure the sleep time.
This is how it can be done:
#include <iostream>
#include <cstdio>
#include <chrono>
#include <thread>
std::chrono::system_clock::time_point a = std::chrono::system_clock::now();
std::chrono::system_clock::time_point b = std::chrono::system_clock::now();
int main()
{
while (true)
{
// Maintain designated frequency of 5 Hz (200 ms per frame)
a = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> work_time = a - b;
if (work_time.count() < 200.0)
{
std::chrono::duration<double, std::milli> delta_ms(200.0 - work_time.count());
auto delta_ms_duration = std::chrono::duration_cast<std::chrono::milliseconds>(delta_ms);
std::this_thread::sleep_for(std::chrono::milliseconds(delta_ms_duration.count()));
}
b = std::chrono::system_clock::now();
std::chrono::duration<double, std::milli> sleep_time = b - a;
// Your code here
printf("Time: %f \n", (work_time + sleep_time).count());
}
}
This code gives me a steady sequence of deltas:
Time: 199.057206
Time: 199.053581
Time: 199.064718
Time: 199.053515
Time: 199.053307
Time: 199.053415
Time: 199.053164
Time: 199.053511
Time: 199.053280
Time: 199.053283
The alternating delta times are arising from a logic problem: you're adding a delay to one frame based on the duration of the frame before (in terms of how the frame durations are reckoned). This means that after a long frame (~200ms) you don't apply a delay and get a short frame (few ms), which then triggers a delay on the next frame giving a long frame, and so on.
I usualy do something like this:
#include <chrono>
#include <iostream>
int main()
{
using clock = std::chrono::steady_clock;
auto next_frame = clock::now();
while(true)
{
next_frame += std::chrono::milliseconds(1000 / 5); // 5Hz
// do stuff
std::cout << std::time(0) << '\n'; // 5 for each second
// wait for end of frame
std::this_thread::sleep_until(next_frame);
}
}
Output: (five for each second value)
1470173964
1470173964
1470173964
1470173964
1470173964
1470173965
1470173965
1470173965
1470173965
1470173965
1470173966
1470173966
1470173966
1470173966
1470173966
This is much like Galik's answer, but it keeps the syntax of the OP's question and doesn't drop down to the C API. Additionally it creates a custom unit for the frame duration which I believe is important for readability:
#include <chrono>
#include <cstdint>
#include <iostream>
#include <thread>
int
main()
{
using namespace std;
using namespace std::chrono;
using frames = duration<int64_t, ratio<1, 5>>; // 5Hz
auto nextFrame = system_clock::now();
auto lastFrame = nextFrame - frames{1};;
while (true)
{
// Perform intersection test
this_thread::sleep_until(nextFrame);
cout << "Time: " // just for monitoring purposes
<< duration_cast<milliseconds>(system_clock::now() - lastFrame).count()
<< "ms\n";
lastFrame = nextFrame;
nextFrame += frames{1};
}
}
This outputs for me:
Time: 200ms
Time: 205ms
Time: 205ms
Time: 203ms
Time: 205ms
Time: 205ms
Time: 200ms
Time: 200ms
Time: 200ms
...
Key things to note:
using frames = duration<int64_t, ratio<1, 5>>;
sleep_until
instead of sleep_for
, which takes care of the unknown of how long it takes to get the real work done..count()
except for I/O, and here's a library to get rid of that./ 1000
).With the addition of the duration I/O library, here is how the above code would be changed:
#include "chrono_io.h"
#include <chrono>
#include <cstdint>
#include <iostream>
#include <thread>
int
main()
{
using namespace date;
using namespace std;
using namespace std::chrono;
using frames = duration<int64_t, ratio<1, 5>>; // 5Hz
auto nextFrame = system_clock::now();
auto lastFrame = nextFrame - frames{1};;
while (true)
{
// Perform intersection test
this_thread::sleep_until(nextFrame);
// just for monitoring purposes
cout << "Time: " << system_clock::now() - lastFrame << '\n';
lastFrame = nextFrame;
nextFrame += frames{1};
}
}
The output would differ depending upon platform (depending upon the "native duration" of system_clock
). On my platform it looks like this:
Time: 200042µs
Time: 205105µs
Time: 205107µs
Time: 200044µs
Time: 205105µs
Time: 200120µs
Time: 204307µs
Time: 205136µs
Time: 201978µs
...