1

What is the best way in C++11 to implement a high-resolution timer that continuously checks for time in a loop, and executes some code after it passes a certain point in time? e.g. check what time it is in a loop from 9am onwards and execute some code exactly at 11am. I require the timing to be precise (i.e. no more than 1 microsecond after 9am).

I will be implementing this program on Linux CentOS 7.3, and have no issues with dedicating CPU resources to execute this task.

seldak
  • 281
  • 3
  • 12
L.Koh
  • 109
  • 1
  • 8
  • @Aconcagua yes. In that case, any suggestions? – L.Koh Sep 16 '19 at 09:59
  • [`std::chrono::high_resolution_clock`](https://en.cppreference.com/w/cpp/chrono/high_resolution_clock): *'represents the clock with the smallest tick period provided by the implementation'*. – Aconcagua Sep 16 '19 at 10:11
  • 3
    Running in a loop appears pretty questionable to me, though, if you need such high precision. At very least, you'll need to make sure that no other processes run on the same core, so that you avoid context switches between threads. Possibly, an interrupt based solution has better chance to reach desired precision. – Aconcagua Sep 16 '19 at 10:14
  • The problem with such ideas is that you typically have more timers firing at whole hours, all of which need CPU resources. But I agree with @Aconcagua. Synchronization with an external clock only makes sense when communicating with another computer, and that isn't going to be instant. Just sending one kilobyte over an 1 Gbit/sec link takes 8 microseconds. – MSalters Sep 16 '19 at 10:36
  • Why do you need microsecond accuracy to start the task? – gavinb Sep 16 '19 at 11:58
  • @Aconcagua thanks. I looked up `std::chrono::high_resolution_clock` prior to posting this question. How can it be used to define a future point in time? Such that at e.g. 9am I indicate to the program that I would like to execute some code at 11am. – L.Koh Sep 16 '19 at 13:49
  • 1
    `auto targetTime = std::chrono::high_resolution_clock::now(); /* now get exact time from your atomic clock! With that, calculate exact distance to 11:00 */ targetTime += duration; while(targetTime - std::chrono::high_resolution_clock::now() > 0) { /* wait */ }` (calculation for yet being positive time difference has been simplified!). – Aconcagua Sep 16 '19 at 14:20
  • 1
    1us! Wow! OK, waiting in a loop is no solution. This is a real time scheduler requirement. So you should think of using RT kernel in that case. But what is the sense of such a scenario. Typically such things are running on embedded devices with synchronized crystal on board. Keeping the OS clock such precise on a normal PC sounds impossible to me. Have you ever checked how much time deviation your RTC has on your system? BTW: How do you synchronize to the real planet time? Using ntp protocol did not offer such precision! – Klaus Sep 16 '19 at 14:47

3 Answers3

2

Instead of implementing this manually, you could use e.g. a systemd.timer. Make sure to specify the desired accuracy which can apparently be as precise as 1us.

user268396
  • 11,576
  • 2
  • 31
  • 26
  • Is it possible to include systemd.timer in a c++ program? As it is part of a larger program, i'm not able to separate the code out into a program which can be put on systemd.timer. – L.Koh Sep 16 '19 at 08:25
  • @L.Koh: A systemd.timer starts a systemd.service. Services start a program, but your already runs. – MSalters Sep 16 '19 at 10:40
  • @L.Koh yes: if your C++ program talks dbus you can also dynamically create & register the timer with `systemd`. – user268396 Sep 16 '19 at 10:47
2

a high-resolution timer that continuously checks for time in a loop,

First of all, you do not want to continuously check the time in a loop; that's extremely inefficient and simply unnecessary.

...executes some code after it passes a certain point in time?

Ok so you want to run some code at a given time in the future, as accurately as possible.

The simplest way is to simply start a background thread, compute how long until the target time (in the desired resolution) and then put the thread to sleep for that time period. When your thread wakes up, it executes the actual task. This should be accurate enough for the vast majority of needs.

The std::chrono library provides calls which make this easy:

Here's a snippet of code which does what you want using the system clock (which makes it easier to set a wall clock time):

// c++ --std=c++11 ans.cpp -o ans

#include <thread>
#include <iostream>
#include <iomanip>

// do some busy work
int work(int count)
{
    int sum = 0;
    for (unsigned i = 0; i < count; i++)
    {
        sum += i;
    }    
    return sum;
}

std::chrono::system_clock::time_point make_scheduled_time (int yyyy, int mm, int dd, int HH, int MM, int SS)
{
    tm datetime = tm{};

    datetime.tm_year = yyyy - 1900; // Year since 1900
    datetime.tm_mon = mm - 1; // Month since January
    datetime.tm_mday = dd; // Day of the month [1-31]
    datetime.tm_hour = HH; // Hour of the day [00-23]
    datetime.tm_min = MM;
    datetime.tm_sec = SS;

    time_t ttime_t = mktime(&datetime);

    std::chrono::system_clock::time_point scheduled = std::chrono::system_clock::from_time_t(ttime_t);

    return scheduled;
}

void do_work_at_scheduled_time()
{
    using period = std::chrono::system_clock::period;

    auto sched_start = make_scheduled_time(2019, 9, 17,  // date
                                           00, 14, 00);  // time

    // Wait until the scheduled time to actually do the work
    std::this_thread::sleep_until(sched_start);

    // Figoure out how close to scheduled time we actually awoke
    auto actual_start = std::chrono::system_clock::now();
    auto start_delta = actual_start - sched_start;
    float delta_ms = float(start_delta.count())*period::num/period::den * 1e3f;

    std::cout << "worker: awoken within " << delta_ms << " ms" << std::endl;

    // Now do some actual work!
    int sum = work(12345);
}

int main()
{
    std::thread worker(do_work_at_scheduled_time);

    worker.join();

    return 0;
}

On my laptop, the typical latency is about 2-3ms. If you use the high_resolution_clock you should be able to get even better results.

There are other APIs you could use too, such as Boost where you could use ASIO to implement high res timeout.

I require the timing to be precise (i.e. no more than 1 microsecond after 9am).

Do you really need it to be accurate to the microsecond? Consider that at this resolution, you will also need to take into account all sorts of other factors, including system load, latency, clock jitter, and so on. Your code can start to execute at close to that time, but that's only part of the problem.

gavinb
  • 19,278
  • 3
  • 45
  • 60
  • Thanks @gavinb. This is helpful. I have previously also explored struct tm, but the issue seems to be that it is not possible to define tm up to at least milliseconds? Please correct me if I am wrong. – L.Koh Sep 17 '19 at 13:33
  • @L.Koh The `struct tm` is only used to define the scheduled wall clock time (in seconds), which is then converted to a `std::chrono::time_point`, which is far more accurate (at least ms or better, depending on the OS/platform). So when the time delta is computed for the sleep time, it will be as precise as `std::chrono::system_clock::period`, microseconds on my machine. But as I mentioned though, there are all sorts of latencies and factors that impact performance. – gavinb Sep 17 '19 at 15:07
0

My suggestion would be to use timer_create(). This allows you to get notified by a signal at a given time. You can then implement your action in the signal handler.

In any case you should be aware that the accuracy of course depends on the system clock accuracy.

nielsen
  • 5,641
  • 10
  • 27