1

I'm currently working on some C++ code that reads from a video file, parses the video/audio streams into its constituent units (such as an FLV tag) and sends it back out in order to "restream" it.

Because my input comes from file but I want to simulate a proper framerate when restreaming this data, I am considering the ways that I might sleep the thread that is performing the read on the file in order to attempt to extract a frame at the given rate that one might expect out of typical 30 or 60 FPS.

One solution is to use an obvious std::this_thread::sleep_for call and pass in the amount of milliseconds depending on what my FPS is. Another solution I'm considering is using a condition variable, and using std::condition_variable::wait_for with the same idea.

I'm a little stuck, because I know that the first solution doesn't guarantee exact precision -- the sleep will last around as long as the argument I pass in but may in theory be longer. And I know that the std::condition_variable::wait_for call will require lock reacquisition which will take some time too. Is there a better solution than what I'm considering? Otherwise, what's the best methodology to attempt to pause execution for as precise a granularity as possible?

Jeff Gong
  • 1,713
  • 3
  • 15
  • 19
  • Much more important than pausing execution with precise granularity is correctly computing how long to pause for. – David Schwartz Feb 21 '19 at 00:24
  • 1
    Use *absolute* timing: https://stackoverflow.com/a/47964169/3807729 – Galik Feb 21 '19 at 01:07
  • Running a thread at a fixed frequency is common for games, such as the "physics engine". For Windows, it's done with a combination of sleep and polling a high frequency timer. Here is a link to [example fixed frequency thread Windows code](https://stackoverflow.com/questions/31323214/how-to-coordinate-threads-properly-based-on-a-fixed-cycle-frequency/31325084#31325084). The example code is written to run as close to fixed frequency as possible, with no drift, (the delays are based on a one time reading of the high frequency timer). I assume Linux has something similar. – rcgldr Feb 21 '19 at 01:15

1 Answers1

1

C++11 Most accurate way to pause execution for a certain amount of time?

This:

auto start = now();
while(now() < start + wait_for);

now() is a placeholder for the most accurate time measuring method available for the system.

This is the analogue to sleep as what spinlock is to a mutex. Like a spinlock, it will consume all the CPU cycles while pausing, but it is what you asked for: The most accurate way to pause execution. There is trade-off between accuracy and CPU-usage-efficiency: You must choose which is more important to have for your program.

why is it more accurate than std::this_thread::sleep_for?

Because sleep_for yields execution of the thread. As a consequence, it can never have better granularity than the process scheduler of the operating system has (assuming there are other processes competing for time).

The live loop shown above which doesn't voluntarily give up its time slice will achieve the highest granularity provided by the clock that is used for measurement.

Of course, the time slice granted by the scheduler will eventually run out, and that might happen near the time we should be resuming. Only way to reduce that effect is to increase the priority of our thread. There is no standard way of affecting the priority of a thread in C++. The only way to get completely rid of that effect is to run on a non-multi-tasking system.

On multi-CPU systems, one trick that you might want to do is to set the thread affinity so that the OS thread won't be migrated to other hard ware threads which would introduce latency. Likewise, you might want to set thread affinity of your other threads to stay off the time measuring thread. There is no standard tool to set thread affinity.


Let T be the time you wish to sleep for and let G be the maximum time that sleep_for could possibly overshoot.

If T is greater than G, then it will be more efficient to use sleep_for for T - G time units, and only use the live loop for the final G - O time units (where O is the time that sleep_for was observed to overshoot).

Figuring out what G is for your target system can be quite tricky however. There is no standard tool for that. If you over-estimate, you'll waste more cycles than necessary. If you under-estimate, your sleep may overshoot the target.


In case you're wondering what is a good choice for now(), the most appropriate tool provided by the standard library is std::chrono::steady_clock. However, that is not necessarily the most accurate tool available on your system. What tool is the most accurate depends on what system you're targeting.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 2
    _"It will consume all the CPU cycles while pausing"_ Ouch!!!!! .......... – πάντα ῥεῖ Feb 21 '19 at 00:20
  • 2
    Well... why is it more accurate than `std::this_thread::sleep_for`? Plus "*`now()` is a placeholder for the most accurate time measuring method available for the system*" seems like a weird way to answer "what's the most accurate way to pause an execution [...]?". Could you please elaborate on your statements and example? – Fureeish Feb 21 '19 at 00:22
  • 1
    @Fureeish I've added an explanation. – eerorika Feb 21 '19 at 00:28
  • 1
    I am not convinced that this is any more accurate then `sleep_for` or `sleep_until` to be honest because the operating system is still going to be preempting the thread, even if you don't voluntarily give it up using `sleep_for`. Also the continual calls to `now()` may take longer than the kernel response time to a `sleep_for` wake up - making it less accurate than waiting. – Galik Feb 22 '19 at 20:31
  • @Galik feel free to prove me wrong :) I think you're over-estimating the complexity of `now()` - or underestimating granularity of the scheduler. You have a point with the pre-emption, although I did also already admit the possibility in the answer. The point is that pre-emption causes delay only if it happens around the time of wakeup; and there is a chance that it doesn't happen. In case of `sleep_for`, you voluntarily give up the time slice that you've been given with 100% probability. So, the worst case accuracy of the live loop is as bad as `sleep_for`, but average case is much better. – eerorika Feb 22 '19 at 23:07
  • 1
    @eerorika Okay, you win. In my tests your spin-timing is considerably more accurate than a `sleep_until` :) – Galik Feb 23 '19 at 00:11