Home > C++11 > Periodic Processing With Standard C++11 Facilities

Periodic Processing With Standard C++11 Facilities

With the addition of the <chrono> and <thread> libraries to the C++11 standard library, programs that require precise, time-synchronized functionality can now be written without having to include any external, non-standard libraries (ACE, Boost, Poco, pthread, etc) into the development environment. However, learning and using the new APIs correctly can be a non-trivial undertaking. But, there’s a reason for having these rich and powerful APIs:

The time facilities are intended to efficiently support uses deep in the system; they do not provide convenience facilities to help you maintain your social calendar. In fact, the time facilities originated with the stringent needs of high-energy physics. Furthermore, language facilities for dealing with short time spans (e.g., nanoseconds) must not themselves take significant time. Consequently, the <chrono> facilities are not simple, but many uses of those facilities can be very simple. – Bjarne Stroustrup (The C++ Programming Language, Fourth Edition).

Many soft and hard real-time applications require chunks of work to be done at precise, periodic intervals during run-time. The figure below models such a program. At the start of each fixed time interval T, “doStuff()” is invoked to perform an application-specific unit of work. Upon completion of the work, the program (or task/thread) “sleeps” under the care of the OS until the next interval start time occurs. If the chunk of work doesn’t complete before the next interval starts, or accurate, interval-to-interval periodicity is not maintained during operation, then the program is deemed to have “failed“. Whether the failure is catastrophic or merely a recoverable blip is application-specific.

PeriodicProcessing

Although the C++11 time facilities support time and duration manipulations down to the nanosecond level of precision, the actual accuracy of code that employs the API is a function of the precision and accuracy of the underlying hardware and OS platform. The C++11 API simply serves as a standard, portable bridge between the coder and the platform.

The figure below shows the effect of platform inaccuracy on programs that need to maintain a fixed, periodic timeline. Instead of “waking up” at perfectly constant intervals of T, some jitter is introduced by the platform. If the jitter is not compensated for, the program will cumulatively drift further and further away from “real-time” as it runs. Even if jitter compensation is applied on an interval by interval basis to prevent drift-creep, there will always be some time inaccuracy present in the application due to the hardware and OS limitations. Again, the specific application dictates whether the timing inaccuracy is catastrophic or merely a nuisance to be ignored.

TimingInaccuracy

The simple UML activity diagram below shows the cruxt of what must be done to measure the “wake-up error” on a given platform.

Time Loop AD

On each pass through the loop:

  • The OS wakes us up (via <thread>)
  • We retrieve the current time (via <chrono>)
  • We compute the error between the current time and the desired time (via <chrono>)
  • We compute the next desired wake-up time (via <chrono>)
  • We tell the OS to put us to sleep until the desired wake-up time occurs (via <thread>)

For your viewing and critiquing pleasure, the source code for a simple, portable C++11  program that measures average  “wakeup error” is shown here:

SleepAccuracySourceCode

The figure below shows the results from a typical program run on a Win7 (VC++13) and Linux (GCC 4.7.2) platform. Note that the clock precision on the two platforms are different. Also note that in the worst case, the 100 millisecond period I randomly chose for the run is maintained to within an average of .1 percent over the 30 second test run. Of course, your mileage may vary depending on your platform specifics and what other services/daemons are running on that platform.

Sleep Errors

The purpose of writing and running this test program was not to come up with some definitive, quantitative results. It was simply to learn and play around with the <chrono> and <thread> APIs; and to discover how qualitatively well the software and hardware stacks can accommodate software components that require doing chunks of work on fixed, periodic, time boundaries.

In case anyone wants to take the source code and run with it, I’ve attached it to the site as a pdf: periodicTask.pdf. You could simulate chunks of work being done within each interval by placing a std::this_thread::sleep_for() within the std::this_thread::sleep_until() loop. You could elevate numLoops and intervalPeriodMillis from compile time constants to command line arguments. You can use  std::minmax_element() on the wakeup error samples.

  1. Ala Mala
    December 27, 2013 at 4:44 am

    Do you happen to know if, and eventually where, I can get a copy of D4P4D? I would really appreciate if you could provide a hint on where I can find it. Thanks!

    • December 27, 2013 at 9:13 am

      Try e-mailing Bill Livingston directly. His e-mail is vitalith “at” att “dot” net. I’m willing to bet that he’ll give you a copy for free.

  2. Ala Mala
    December 27, 2013 at 10:16 am

    Ok, I’ll give it a try. Thanks.

    • December 27, 2013 at 11:48 am

      If you get a chance, lemme know what the outcome is.

  3. Mikel
    November 20, 2020 at 12:20 pm

    My results are around 180 microseconds average delay. Am I doing something wrong? It is not even close to your 20 and 96 microseconds. How do you compile it?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: