0

I try to make sure the execution time of each loop to 10ms with usleep , but sometimes it exceeds 10ms.

I have no idea how to solve this problem, is it proper to use usleep and gettimeofday in this case?

Please help my find out what i missed.

Result: 0.0127289 0.0136499 0.0151598 0.0114031 0.014801

double tvsecf(){
     struct timeval tv;
     double asec;

     gettimeofday(&tv,NULL);
     asec = tv.tv_usec;
     asec /= 1e6;
     asec += tv.tv_sec;

     return asec;
}
int main(){
    double t1 ,t2;
    t1 = tvsecf();
    for(;;){
        t2= tvsecf();
        if(t2-t1 >= 0.01){
            if(t2-t1 >= 0.011)
                cout << t2-t1 <<endl;
            t1 = tvsecf();
        }
        usleep(100);
    }
}
zhiyo
  • 11
  • 3
  • 1
    First, you have to decide which language you are using… – sidyll Jun 28 '18 at 17:49
  • Usually sleeps are only guaranteed to sleep for *at least* as long as you asked for - they may sleep longer. – Jesper Juhl Jun 28 '18 at 17:51
  • I think `nanosleep` is the preferred sleep. – Christian Gibbons Jun 28 '18 at 17:53
  • 2
    This sounds like a [XY problem](http://xyproblem.info). What are you *actually* trying to achieve? – Jesper Juhl Jun 28 '18 at 17:54
  • 1
    If you really need to do something every 10ms without fail (and don't just think you do), then you're going to need to run your program on a real-time OS – Jeremy Friesner Jun 28 '18 at 17:58
  • Just sleeping for a fixed amount of time at the end of the loop disregards the time you *already spent* running the top of the loop. There is no way that strategy would run each iteration on a fixed 10ms beat unless the time spent in the rest of the loop is zero. At the very least you would want to sleep for "10ms - time_already_spent_this_iteration". – Jesper Juhl Jun 28 '18 at 18:29
  • The reason i want to control the loop execution time is to control adc sample rate, in this case 10ms to achieve 100sps sample rate – zhiyo Jun 28 '18 at 18:31
  • Sorry i didn't describe my actual problem first, what i'm looking for is more close to the average iteration time. – zhiyo Jun 28 '18 at 19:29

3 Answers3

3

To keep the loop overhead (which is generally unknown) from constantly accumulating error, you can sleep until a time point, instead of for a time duration. Using C++'s <chrono> and <thread> libraries, this is incredibly easy:

#include <chrono>
#include <iostream>
#include <thread>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    auto t0 = steady_clock::now() + 10ms;
    for (;;)
    {
        this_thread::sleep_until(t0);
        t0 += 10ms;
    }
}

One can dress this up with more calls to steady_clock::now() in order to ascertain the time between iterations, and perhaps more importantly, the average iteration time:

#include <chrono>
#include <iostream>
#include <thread>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    using dsec = duration<double>;
    auto t0 = steady_clock::now() + 10ms;
    auto t1 = steady_clock::now();
    auto t2 = t1;
    constexpr auto N = 1000;
    dsec avg{0};
    for (auto i = 0; i < N; ++i)
    {
        this_thread::sleep_until(t0);
        t0 += 10ms;
        t2 = steady_clock::now();
        dsec delta = t2-t1;
        std::cout << delta.count() << "s\n";
        avg += delta;
        t1 = t2;
    }
    avg /= N;
    cout << "avg = " << avg.count() << "s\n";
}

Above I've added to the loop overhead by doing more things within the loop. However the loop is still going to wake up about every 10ms. Sometimes the OS will wake the thread late, but next time the loop automatically adjusts itself to sleep for a shorter time. Thus the average iteration rate self-corrects to 10ms.

On my machine this just output:

...
0.0102046s
0.0128338s
0.00700504s
0.0116826s
0.00785826s
0.0107023s
0.00912614s
0.0104725s
0.010489s
0.0112545s
0.00906409s
avg = 0.0100014s
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
0

From the usleep man page:

The sleep may be lengthened slightly by any system activity or by the time spent processing the call or by the granularity of system timers.

If you need high resolution: with C on Unix (or Linux) check out this answer that explains how to use high resolution timers using clock_gettime.

Edit: As mentioned by Tobias nanosleep may be a better option:

Compared to sleep(3) and usleep(3), nanosleep() has the following
advantages: it provides a higher resolution for specifying the sleep
interval; POSIX.1 explicitly specifies that it does not interact with
signals; and it makes the task of resuming a sleep that has been
interrupted by a signal handler easier.
Michael Powers
  • 1,970
  • 1
  • 7
  • 12
  • 1
    Also from usleep man page. POSIX.1-2001 declares this function obsolete use nanosleep(2) instead. POSIX.1-2008 removes the specification of usleep() – Tobias Jun 28 '18 at 18:31
0

There is no way to guarantee 10ms loop time. All sleeping functions sleeps for at least wanted time. For a portable solution use std::this_thread::sleep_for

#include <iostream>
#include <chrono>
#include <thread>

int main()
{
    for (;;) {
        auto start = std::chrono::high_resolution_clock::now();
        std::this_thread::sleep_for(std::chrono::milliseconds{10});
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> elapsed = end-start;
        std::cout << "Waited " << elapsed.count() << " ms\n";
    }
}

Depending on what you are trying to do take a look at Howard Hinnants date library.

Tobias
  • 835
  • 1
  • 6
  • 11