Hello,

Just out of curiosity, I wrote this program to test the latency using a
vanilla 3.4 kernel's "real-time scheduler". Maybe it is of interest to
someone on this list.

$ uname -srvmp
Linux 3.3.8 #7 SMP PREEMPT Tue Jun 26 03:08:40 CEST 2012 x86_64 Intel(R)
Core(TM) i3-2120T CPU @ 2.60GHz

Tests were made while running a 1080p x264 movie and "shaking" a large
glxgears window.

Sample running as normal user without permission to change to realtime
scheduler, i.e. running under the normal scheduler (SCHED_OTHER):

$ ./rt_test
Setting SCHED priority 1
Could not set scheduler: Operation not permitted
CLOCK_REALTIME resolution: 1 ns
Establishing handler for signal 34
timer ID is 0x1ae6010
...
Min, max diff  -686,   694 µs; cumulative diff 1990 µs; overruns 0;
calls 70913
Min, max diff  -827,  1257 µs; cumulative diff 4003 µs; overruns 2;
calls 71970
Min, max diff  -645,  1193 µs; cumulative diff 4994 µs; overruns 1;
calls 73028
Min, max diff  -629,   635 µs; cumulative diff 4996 µs; overruns 0;
calls 74087
Min, max diff   -48,    65 µs; cumulative diff 4997 µs; overruns 0;
calls 75146
Min, max diff   -29,    34 µs; cumulative diff 4990 µs; overruns 0;
calls 76204
Min, max diff  -253,   160 µs; cumulative diff 4995 µs; overruns 0;
calls 77263
Min, max diff  -854,   868 µs; cumulative diff 4996 µs; overruns 0;
calls 78323
Min, max diff  -812,  1812 µs; cumulative diff 5990 µs; overruns 1;
calls 79381
Min, max diff  -635,   371 µs; cumulative diff 5990 µs; overruns 0;
calls 80441
Min, max diff  -536,   540 µs; cumulative diff 6000 µs; overruns 0;
calls 81500
Min, max diff  -855,  1229 µs; cumulative diff 6988 µs; overruns 1;
calls 82558
Min, max diff  -305,   435 µs; cumulative diff 7002 µs; overruns 0;
calls 83618
Min, max diff  -293,   304 µs; cumulative diff 6995 µs; overruns 0;
calls 84677
Min, max diff  -644,   647 µs; cumulative diff 6993 µs; overruns 0;
calls 85737


Sample running under real-time scheduler (SCHED_FIFO):
# ulimit -r 1
# ./rt_test
Setting SCHED priority 1
CLOCK_REALTIME resolution: 1 ns
Establishing handler for signal 34
timer ID is 0x1236010
...
Min, max diff   -24,    36 µs; cumulative diff 2 µs; overruns 0; calls 35173
Min, max diff   -15,    20 µs; cumulative diff -3 µs; overruns 0; calls
36178
Min, max diff   -30,    24 µs; cumulative diff -3 µs; overruns 0; calls
37183
Min, max diff   -17,    14 µs; cumulative diff -1 µs; overruns 0; calls
38187
Min, max diff   -27,    22 µs; cumulative diff -4 µs; overruns 0; calls
39192
Min, max diff   -68,    80 µs; cumulative diff 2 µs; overruns 0; calls 40199
Min, max diff   -26,    32 µs; cumulative diff 0 µs; overruns 0; calls 41204
Min, max diff   -23,    29 µs; cumulative diff -3 µs; overruns 0; calls
42209
Min, max diff   -29,    28 µs; cumulative diff 3 µs; overruns 0; calls 43215
Min, max diff   -24,    25 µs; cumulative diff -3 µs; overruns 0; calls
44220
Min, max diff   -26,    28 µs; cumulative diff 2 µs; overruns 0; calls 45225
Min, max diff   -19,    25 µs; cumulative diff -3 µs; overruns 0; calls
46231
Min, max diff   -47,    57 µs; cumulative diff -1 µs; overruns 0; calls
48242


It looks good enough for the servo thread, at least on my computer!
Here's the program, in case you want to try it yourself:

Compile with
$ gcc -Wall -lrt rt_test.c -o rt_test

---
/*
 * Latency/precision test program for POSIX timers under Linux
 * real-time scheduling (SCHED_FIFO).
 *
 * Based on the sample code in 'man 2 timer_create',
 * copyright (c) 2009 Linux Foundation, written by Michael Kerrisk
 *     <mtk.manpa...@gmail.com>
 * and released under the "Verbatim" License:
 * https://www.kernel.org/doc/man-pages/licenses.html#verbatim
 *
 * Modifications for this usage by Arvid Brodin <arv...@kth.se>.
 */

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <time.h>
#include <errno.h>

#define CLOCKID CLOCK_REALTIME
#define SIG SIGRTMIN
#define PERIOD          1000000


#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)

static inline int min(int a, int b)
{
        return (a < b) ? a : b;
}

static inline int max(int a, int b)
{
        return (a > b) ? a : b;
}


/* Global variable to keep track of latency statistics etc. */
static struct {
        int mindiff;    // Maximum negative deviation
        int maxdiff;    // Maximum positive deviation
        int cumdiff;    // Cumulative deviation from first call
        int calls;      // Total number of calls made
        int overruns;   // Number of completely missed calls
        int last_ns;
} period_info;

static void print_clockinfo()
{
        struct timespec ts;

        if (clock_getres(CLOCK_REALTIME, &ts) != 0) {
                printf("Could not get clock info.\n");
                return;
        }

        printf("CLOCK_REALTIME resolution: %ld ns\n", ts.tv_nsec);
}

/*
 * Print statistics. Called from main loop.
 */
static void print_diffs()
{
        printf("Min, max diff %5d, %5d µs; cumulative diff %d µs; overruns %d;
calls %d\n",
                                        period_info.mindiff / 1000,
                                        period_info.maxdiff / 1000,
                                        period_info.cumdiff / 1000,
                                        period_info.overruns,
                                        period_info.calls);
        period_info.mindiff = 0;
        period_info.maxdiff = 0;
        period_info.overruns = 0;
}


static struct sigaction sa;

/*
 * Signal handler. Called every PERIOD ns.
 */
static void handler(int sig, siginfo_t *si, void *uc)
{
        timer_t *tidp;
        struct timespec ts;
        int overruns;
        int diff;

        period_info.calls += 1;

        tidp = si->si_value.sival_ptr;
        overruns = timer_getoverrun(*tidp);
        if (overruns > 0)
                period_info.overruns += overruns;

        clock_gettime(CLOCKID, &ts);
        if (period_info.last_ns < 0)
                // First call
                period_info.last_ns = ts.tv_nsec - PERIOD;
        else if (ts.tv_nsec < period_info.last_ns)
                // Seconds wrapped
                period_info.last_ns -= 1000000000;

        diff = ts.tv_nsec - period_info.last_ns - PERIOD;
        period_info.mindiff = min(period_info.mindiff, diff);
        period_info.maxdiff = max(period_info.maxdiff, diff);
        period_info.cumdiff += diff;

        period_info.last_ns = ts.tv_nsec;
}

/*
 * Signal delivery causes system/library calls to return prematurely.
 * See 'man 7 signal' for details. This function repeatedly call
 * nanosleep() until (at least) 'sec' seconds has passed.
 */
static void sigsafe_sleep(int sec)
{
        struct timespec rqtp, rmtp;
        struct timespec *ts_req, *ts_rem, *ts_tmp;
        int res;

        rqtp.tv_sec = sec;
        rqtp.tv_nsec = 0;

        ts_req = &rqtp;
        ts_rem = &rmtp;
        res = nanosleep(ts_req, ts_rem);
        while (res < 0 && errno == EINTR) {
                ts_tmp = ts_req;
                ts_req = ts_rem;
                ts_rem = ts_tmp;
                res = nanosleep(ts_req, ts_rem);
        }
}

int main(int argc, char *argv[])
{
        struct sched_param sched_param;
        timer_t timerid;
        struct sigevent sev;
        struct itimerspec its;
        int res;
        const int sched_policy = SCHED_FIFO;

        // Scheduler
        sched_param.sched_priority = sched_get_priority_min(sched_policy);
        printf("Setting SCHED priority %d\n", sched_param.sched_priority);
        res = sched_setscheduler(0, sched_policy, &sched_param);
        if (res < 0)
                perror("Could not set scheduler");

        // Some init
        print_clockinfo();
        period_info.mindiff = 0;
        period_info.maxdiff = 0;
        period_info.cumdiff = 0;
        period_info.last_ns = -1;

        // Establish handler for timer signal
        printf("Establishing handler for signal %d\n", SIG);
        sa.sa_flags = SA_SIGINFO;
        sa.sa_sigaction = handler;
        sigemptyset(&sa.sa_mask);
        if (sigaction(SIG, &sa, NULL) == -1)
                errExit("sigaction");

        // Create the timer
        sev.sigev_notify = SIGEV_SIGNAL;
        sev.sigev_signo = SIG;
        sev.sigev_value.sival_ptr = &timerid;
        if (timer_create(CLOCKID, &sev, &timerid) == -1)
                errExit("timer_create");
        printf("timer ID is 0x%lx\n", (long) timerid);

        // Start the timer
        its.it_value.tv_sec = 0;
        its.it_value.tv_nsec = PERIOD;
        its.it_interval.tv_sec = its.it_value.tv_sec;
        its.it_interval.tv_nsec = its.it_value.tv_nsec;

        if (timer_settime(timerid, 0, &its, NULL) == -1)
                errExit("timer_settime");

        while (1) {
                sigsafe_sleep(1);
                print_diffs();
        }

        // Not reached
        exit(EXIT_SUCCESS);
}
---

-- 
Arvid

------------------------------------------------------------------------------
Everyone hates slow websites. So do we.
Make your web apps faster with AppDynamics
Download AppDynamics Lite for free today:
http://p.sf.net/sfu/appdyn_d2d_mar
_______________________________________________
Emc-developers mailing list
Emc-developers@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-developers

Reply via email to