utl::sleep
utl::sleep implements precise sleep functions.
The main feature is a hybrid sleep function that uses a combination of system sleep, busy-waiting and some statistics to wait with an accuracy close to that of a spinlock, while keeping the thread free most of the time.
The idea is largely based on this excellent blogpost, which introduces the approach and presents a strong case for its robustness on most modern systems.
Definitions
template <class Rep, class Period> void system( std::chrono::duration<Rep, Period> duration);
template <class Rep, class Period> void spinlock(std::chrono::duration<Rep, Period> duration);
template <class Rep, class Period> void hybrid( std::chrono::duration<Rep, Period> duration);
Methods
template <class Rep, class Period> void system( std::chrono::duration<Rep, Period> duration); template <class Rep, class Period> void spinlock(std::chrono::duration<Rep, Period> duration); template <class Rep, class Period> void hybrid( std::chrono::duration<Rep, Period> duration);
Three sleep implementations functionally similar to std::this_thread::sleep_for()
, which cover different ranges of use-cases. Below is a summary table of their properties:
Sleep | Precision | CPU usage | Description |
---|---|---|---|
system() |
Imprecise | ~0% | Alias for std::this_thread::sleep_for() |
spinlock() |
Very precise | 100% | Busy-waiting loop |
hybrid() |
Precise | ~5% | Hybrid approach that loops short system sleep, statistically estimates its error on the fly and uses spinlock to finish the last few % |
Examples
Comparing sleep precision
[ Run this code ]
using ms = std::chrono::duration<double, std::milli>;
constexpr int repeats = 6;
constexpr ms sleep_duration = ms(16.67);
const auto measure_time = [](auto sleep_function) {
for (int i = 0; i < repeats; ++i) {
const auto start = std::chrono::steady_clock::now();
sleep_function(sleep_duration);
const auto end = std::chrono::steady_clock::now();
std::cout << ms(end - start).count() << " ms\n";
}
};
std::cout << "Sleeping for 16.67 ms.\n";
std::cout << "\n--- sleep::system() ---\n";
measure_time(utl::sleep::system<double, std::milli>);
std::cout << "\n--- sleep::spinlock() ---\n";
measure_time(utl::sleep::spinlock<double, std::milli>);
std::cout << "\n--- sleep::hybrid() ---\n";
measure_time(utl::sleep::hybrid<double, std::milli>);
Output:
Sleeping for 16.67 ms.
--- sleep::system() ---
16.7649 ms
16.7411 ms
16.7406 ms
16.7338 ms
16.7308 ms
16.7338 ms
--- sleep::spinlock() ---
16.6703 ms
16.6702 ms
16.6704 ms
16.6703 ms
16.6703 ms
16.6701 ms
--- sleep::hybrid() ---
16.6723 ms
16.6723 ms
16.6725 ms
16.6723 ms
16.6721 ms
16.6721 ms