QRTP -- realtime question

Hi,

I faced the following problem while I was playing with QRTP: when I
call
nanosleep (or select) function with an argument of n ms, I figured out
that indeed it takes (n+2) msec…

If I tell a process to sleep 1ms and if it sleeps for 3ms, I would NOT
call this behaviour realtime… Can somebody tell me where this 2ms
delay
is coming from?

Baris

Here is the piece of code I wrote to test this:

/* test.c */

#include <time.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

main()
{
int i1, i2, i3, retval;
struct timespec n;
struct timeval tv1, tv2, tv3, st;

n.tv_sec = 0;
n.tv_nsec = 110001000; /* 1 msec */

st.tv_sec = 0;
st.tv_usec = 11000; / 1 msec */

for (;:wink:
{
i1 = gettimeofday(&tv1, 0);

nanosleep(&n, 0);

i2 = gettimeofday(&tv2, 0);

retval = select(1, NULL, NULL, NULL, &st);

i3 = gettimeofday(&tv3, 0);

printf("-> nanosleep: dt = %d usec.\n",
(tv2.tv_sec-tv1.tv_sec)*1000000 + (tv2.tv_usec -
tv1.tv_usec));
printf("-> select: dt = %d usec\n",
(tv3.tv_sec-tv2.tv_sec)*1000000 + (tv3.tv_usec -
tv2.tv_usec));

sleep(1);
}
}

/* end test.c */

output:

./test

→ nanosleep: dt = 3000 usec
→ select: dt = 2999 usec
→ nanosleep: dt = 2999 usec
→ select: dt = 3000 usec
→ nanosleep: dt = 2999 usec
→ select: dt = 3000 usec
→ nanosleep: dt = 3000 usec
→ select: dt = 2999 usec
→ nanosleep: dt = 3000 usec
→ select: dt = 2999 usec
→ nanosleep: dt = 3000 usec
→ select: dt = 2999 usec

Baris Dundar <dundar@eecs.berkeley.edu> wrote:

Hi,

I faced the following problem while I was playing with QRTP: when I
call
nanosleep (or select) function with an argument of n ms, I figured out
that indeed it takes (n+2) msec…

If I tell a process to sleep 1ms and if it sleeps for 3ms, I would NOT
call this behaviour realtime… Can somebody tell me where this 2ms
delay
is coming from?

What you are running into is the “hidden” costs of doing what you
are trying to do. For instance, nanlosleep() has to create timers
and do other work and it must do this on every cycle. I tried your
code on my machine running linux and it came in at (n+6) msec. The
use of select() has very simular overhead that you don’t see. Also,
things can only really be as accurate as the resolution of the
system clock (I think it is 1ms, at least it is on my machine).

So, I present to you some code that uses pre-setup timers to do a
very simular test. I have the code setup to fire on a 5msec clock
since 1msec was longer then it took for the printf() to write out data
so the clock pulses got queued on me!! This is taken from the helpviewer
page for timer_create() with some changes made by me for this example…


chris

/*

  • Demonstrate how to set up a timer that, on expiry, sends us a pulse.
    */

#include <stdio.h>
#include <time.h>
#include <sys/netmgr.h>
#include <sys/neutrino.h>
#include <time.h>
#include <sys/time.h>

#define MY_PULSE_CODE _PULSE_CODE_MINAVAIL

static long int OffsetSeconds;

int init_myclock( void )
{
struct timeval tval;

gettimeofday( &tval, NULL );
OffsetSeconds = tval.tv_sec;
return 0;
}

long int myclock( void )
{
struct timeval tval;

gettimeofday( &tval, NULL );
tval.tv_sec -= OffsetSeconds;

return( ( tval.tv_sec * 1000000 ) + tval.tv_usec );
}



typedef union {
struct _pulse pulse;
/* your other message structures would go
here too */
} my_message_t;

main()
{
struct sigevent event;
struct itimerspec itime;
timer_t timer_id;
int chid;
int rcvid;
int toggle;
my_message_t msg;
long int t1, t2;



chid = ChannelCreate(0);

event.sigev_notify = SIGEV_PULSE;
event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0,
chid,
_NTO_SIDE_CHANNEL, 0);
event.sigev_priority = getprio(0);
event.sigev_code = MY_PULSE_CODE;
timer_create(CLOCK_REALTIME, &event, &timer_id);

itime.it_value.tv_sec = 0;
itime.it_value.tv_nsec = 5000000;

itime.it_interval.tv_sec = 0;
itime.it_interval.tv_nsec = 5000000;

timer_settime(timer_id, 0, &itime, NULL);

/*

  • As of the timer_settime(), we will receive our pulse
  • in 0.001 seconds (the itime.it_value) and every 1.5
  • seconds thereafter (the itime.it_interval)
    */

init_myclock();
toggle = 0;

for (;:wink:
{
rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
if (rcvid == 0)
{
if (msg.pulse.code == MY_PULSE_CODE)
{
if( toggle )
{
t2 = myclock();
printf( “%d usec between each pulse.\n”, t2 - t1 );
toggle = 0;
}
else
{
t1 = myclock();
toggle = 1;
}
}
}
}
}

\

cdm@qnx.com > “The faster I go, the behinder I get.”

Chris McKillop – Lewis Carroll –
Software Engineer, QSSL
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Chris,

What you are running into is the “hidden” costs of doing what you
are trying to do. For instance, nanlosleep() has to create timers
and do other work and it must do this on every cycle. I tried your
code on my machine running linux and it came in at (n+6) msec. The
use of select() has very simular overhead that you don’t see. Also,
things can only really be as accurate as the resolution of the
system clock (I think it is 1ms, at least it is on my machine).

Linux kernel has a default operating frequency of 100Hz (or 10ms), this
value is defined in /usr/src/linux/include/asm-i386/param.h, so in Linux
it is possible to get faster response by changing this HZ value… but
there is a price you have to pay if you change it from 10ms to 1ms. I am
kind of new to QNX and I was wondering if it is possible to change the
resolution of system clock in QNX.

BTW, thanks for the timer example…

Baris

Baris Dundar <dundar@eecs.berkeley.edu> wrote:

Chris,

What you are running into is the “hidden” costs of doing what you
are trying to do. For instance, nanlosleep() has to create timers
and do other work and it must do this on every cycle. I tried your
code on my machine running linux and it came in at (n+6) msec. The
use of select() has very simular overhead that you don’t see. Also,
things can only really be as accurate as the resolution of the
system clock (I think it is 1ms, at least it is on my machine).

Linux kernel has a default operating frequency of 100Hz (or 10ms), this
value is defined in /usr/src/linux/include/asm-i386/param.h, so in Linux
it is possible to get faster response by changing this HZ value… but
there is a price you have to pay if you change it from 10ms to 1ms. I am
kind of new to QNX and I was wondering if it is possible to change the
resolution of system clock in QNX.

See ClockPeriod() in the C library reference manual. You must run
as root (userid 0) for this function to work. The time slice
for roundrobin scheduling is 4 * this resolution (the multiplier, 4,
is fixed).

BTW, thanks for the timer example…

Baris

Baris Dundar <dundar@eecs.berkeley.edu> wrote:

Chris,

What you are running into is the “hidden” costs of doing what you
are trying to do. For instance, nanlosleep() has to create timers
and do other work and it must do this on every cycle. I tried your
code on my machine running linux and it came in at (n+6) msec. The
use of select() has very simular overhead that you don’t see. Also,
things can only really be as accurate as the resolution of the
system clock (I think it is 1ms, at least it is on my machine).

Linux kernel has a default operating frequency of 100Hz (or 10ms), this
value is defined in /usr/src/linux/include/asm-i386/param.h, so in Linux
it is possible to get faster response by changing this HZ value… but
there is a price you have to pay if you change it from 10ms to 1ms. I am
kind of new to QNX and I was wondering if it is possible to change the
resolution of system clock in QNX.

BTW, thanks for the timer example…

No problem. And yes you can change the clock at runtime. ClockPeriod()
is the API you want to play with. You can drop it to 500us, but you have
to be root to do this, for obvious reasons. :slight_smile:

Something else I thought about, if you set the process priorty of YOUR
example to 12, I bet it ALWAYS returns in the same # of ms, No matter
what else is going on in the system. This is what realtime is all about;
predicatable timings no matter what else is happening (save for some
bad driver turning off all interrupts!). The same can not be said about
Linux or other general purpose OSes.

chris

cdm@qnx.com > “The faster I go, the behinder I get.”

Chris McKillop – Lewis Carroll –
Software Engineer, QSSL
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Baris Dundar <dundar@eecs.berkeley.edu> wrote:

I faced the following problem while I was playing with QRTP: when I
call
nanosleep (or select) function with an argument of n ms, I figured out
that indeed it takes (n+2) msec…

If I tell a process to sleep 1ms and if it sleeps for 3ms, I would NOT
call this behaviour realtime… Can somebody tell me where this 2ms
delay
is coming from?

I wrote the following up for someone else several months ago to explain
the same thing. Enjoy…

(BTW, the default tick rate for QNX/Neutrino is 1ms if the CPU clock
rate is 40MHz or better, 10ms otherwise).

The following piece of code:

for ( i=0; i<1000; i++ )
delay(1)

With timer resolution set to .5 ms this loop last 2 seconds (???)
With timer resolution set to 1ms this loop last 3 seconds (???)

You’re seeing timer quantization (sp?) error. Let us consider the
1ms tick rate. First off, POSIX says that it’s OK to delay too much,
but it’s not OK to delay too little. Since the calling of delay() is
asynchronous with the running of the clock interrupt, that means that
we have to add one clock tick to a relative delay to ensure the correct
amount of time (consider what would happen if we didn’t and a one tick
delay was requested just before the clock interrupt went off). That normally
adds half a tick extra delay on average. This code isn’t average. Since
the thread gets woken up by the clock interrupt, it’s now synchronized
with it and almost immediately delays again. That means that you see
the worst case of getting almost a full extra tick of delay each time.

OK, that should make the loop last 2 seconds, where’s the extra second
coming from? The problem is that when you request a 1ms tick rate, we
may not be able to actually give it too you because of the frequency
of the input clock to the timer hardware. In those cases we choose the
closest number that’s faster than what you requested. In terms of IBM
PC hardware, requesting a 1ms tick rate actually gets you 999,847 nanoseconds
between each tick. With the requested delay, that gives us the following:

1,000,000ns + 999,847ns = 1,999,847ns of actual delay.

1,999,847ns / 999,847ns = 2.000153 ticks before the timer expires

Since we only expire timers at a clock interrupt, ceil(2.000153) gives
us that each delay(1) call actually waits:

999,847ns * 3 = 2,999,541ns

Multiply that by a 1000 for the loop count and you get a total loop time
of 2.999541 seconds.

Things are similar for the clock rate of 0.5ms. The actual period is
499,504ns per tick. If you perform the above calculations, you’ll get a
total delay of about 2 seconds.

If you bump the call to delay(10) and reduce the loop count to 100
to minimize the error, you’ll get a total loop time much closer to the
1 second you expect.


Brian Stecher (bstecher@qnx.com) QNX Software Systems, Ltd.
phone: +1 (613) 591-0931 (voice) 175 Terence Matthews Cr.
+1 (613) 591-3579 (fax) Kanata, Ontario, Canada K2M 1W8