Search code examples
c++11g++thread-sleep

std::this_thread::sleep_for() and nanoseconds


If I put two calls side-by-side to determine the smallest measurable time duration:

// g++ -std=c++11 -O3 -Wall test.cpp
#include <chrono>
typedef std::chrono::high_resolution_clock hrc;

hrc::time_point start = hrc::now();
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "duration: " << duration.count() << " ns" << std::endl;

I've run this thousands of times in a loop, and I consistently get 40 ns +/- 2 ns on my particular 3.40GHz desktop.

However, when I look to see what is the shortest time I can sleep:

#include <thread>

hrc::time_point start = hrc::now();
std::this_thread::sleep_for( std::chrono::nanoseconds(1) );
hrc::time_point end   = hrc::now();
std::chrono::nanoseconds duration = end - start;
std::cout << "slept for: " << duration.count() << " ns" << std::endl;

This tells me I slept on average 55400 nanoseconds, or 55.4 microseconds. Much greater than the time I expected.

Putting the above code into a for() loop, I tried sleeping for different amounts, and this is the result:

  • sleep_for( 4000 ns ) => slept for 58000 ns
  • sleep_for( 3000 ns ) => slept for 57000 ns
  • sleep_for( 2000 ns ) => slept for 56000 ns
  • sleep_for( 1000 ns ) => slept for 55000 ns
  • sleep_for( 0 ns ) => slept for 54000 ns
  • sleep_for( -1000 ns ) => slept for 313 ns
  • sleep_for( -2000 ns ) => slept for 203 ns
  • sleep_for( -3000 ns ) => slept for 215 ns
  • sleep_for( -4000 ns ) => slept for 221 ns

Some questions I have:

  • What could explain these numbers?
  • Why does sleeping for a negative amount of time return 200+ ns, while sleeping for 0+ nanoseconds results in 50,000+ nanoseconds?
  • Is negative numbers as a sleep time a documented/supported feature, or did I accidentally stumble across some strange bug I cannot rely upon?
  • Is there a better C++ sleep call which would give me more consistent/predictable sleep times?

Solution

  • What could explain these numbers?

    There's a pretty obvious pattern, all your results are consistently 54000ns greater than the time you request to sleep. If you look at how GCC's this_thread::sleep_for() is implemented on GNU/Linux you'll see it just uses nanospleep and as Cubbi's comment says, calling that function can take around 50000ns. I would guess some of that cost is making a system call, so switching from user-space to the kernel and back.

    Why does sleeping for a negative amount of time return 200+ ns, while sleeping for 0+ nanoseconds results in 50,000+ nanoseconds?

    At a guess I'd say that the C library checks for the negative number and doesn't make a system call.

    Is negative numbers as a sleep time a documented/supported feature, or did I accidentally stumble across some strange bug I cannot rely upon?

    The standard doesn't forbid passing negative arguments, so it is allowed, and the function should return "immediately" because the time specified by the relative timeout has already passed. You can't rely on negative arguments returning faster than non-negative arguments though, that's an artefact of your specific implementation.

    Is there a better C++ sleep call which would give me more consistent/predictable sleep times?

    I don't think so - if I knew of one then we'd be using it in GCC to implement this_thread::sleep_for().

    Edit: In more recent versions of GCC's libstdc++ I added:

    if (__rtime <= __rtime.zero())
      return;
    

    so there will be no system call when a zero or negative duration is requested.