Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 Dec 2016 21:35:32 -0700
From:      Ian Lepore <ian@freebsd.org>
To:        Goran =?iso-8859-2?Q?Meki=E6?= <meka@tilda.center>
Cc:        freebsd-hackers@freebsd.org
Subject:   Re: How to use sem_timedwait?
Message-ID:  <1481776532.1889.461.camel@freebsd.org>
In-Reply-To: <1481775511.1889.450.camel@freebsd.org>
References:  <20161214074228.zh6q5zya2gszw4g6@hal9000.meka.no-ip.org> <1481748960.1889.398.camel@freebsd.org> <20161215002906.mllorgvvuovbdtze@hal9000.meka.no-ip.org> <1481775511.1889.450.camel@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, 2016-12-14 at 21:18 -0700, Ian Lepore wrote:
> On Thu, 2016-12-15 at 01:29 +0100, Goran Mekiĉ wrote:
> > 
> > On Wed, Dec 14, 2016 at 01:56:00PM -0700, Ian Lepore wrote:
> > > [...]
> 
> Making a guess here:  Is your actual goal to wake up periodically
> with the period between wakeups as accurate as possible?  If so, a
> better mechanism might be to use kqueue(2)/kevent(2)
> with EVFILT_TIMER events.  With EVFILT_TIMER events, the wakeups are
> scheduled as if aligned to a grid -- even if one wakeup is a bit late
> due to system workload, the next wakeup after it will still be
> properly aligned to the original grid.  For example, if you ask for a
> wakeup once per millisecond and some wake happens after 1.2mS, the
> next wakeup will be .8mS after that; the phase of the wakeups doesn't
> shift over time.

I just dug up some old source code for testing kevent timers, included
below.  Good news and bad news...  The good news is that it works
perfectly on an arm system running -current:

    root@imx6    :~ # ./kevent_evfilt_timer
    nsec = 10000058742 for 10000 loops of 1000000 nsec

The extra 58uS is fixed overhead for getting the ending time; if the
loop runs for 100 seconds instead of 10 the extra time is still 55-
60uS.

The bad news is that it doesn't work right on amd64 running 10-stable:

    revolution > ./kevent_evfilt_timer 
    nsec = 9313236220 for 10000 loops of 1000000 nsec

I don't have any other x86 systems handy to test it on right now, but
it's disturbing that 10 seconds worth of 1mS sleeps takes less than 10
seconds.  A strange thing here is that the *ratio* of the undersleeping
is fixed, running the loop for 100 seconds instead of 10 gives:

    revolution > ./kevent_evfilt_timer 
    nsec = 93132267106 for 100000 loops of 1000000 nsec

The program....

/*
 * This tests kevent timer events to make sure that they are properly
 * metronomic.  That is, each event should occur at the requested
 * interval following the prior event, and in particular the phase of
 * event delivery should not drift because of the time it takes to
 * process the event and schedule the next one in the kernel.  (i.e.,
 * this is a userland test of kernel kevent timer performance.)
 * Because it's hard to measure the tiny increments of time between
 * each sleep, we run a loop that takes several seconds and look for
 * the total elapsed time to be increment*loopcount plus a tiny bit of
 * overhead for getting the ending time.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>

int
main(int argc, char ** argv)
{
    int i, interval;
    int64_t nsec;
    int kq,num_events;
    struct kevent inqueue;
    struct kevent outqueue;
    struct timespec start,end;

    // argument is timer interval in nanoseconds.
    if (argc > 1)
        interval = strtoul(argv[1], NULL, 0);
    else
        interval = 1000000;

    if ((kq = kqueue()) == -1) {
        fprintf(stderr, "kqueue errno = %s", strerror(errno));
        exit(EXIT_FAILURE);
    }
    EV_SET(&inqueue, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, 
        NOTE_NSECONDS, interval, 0);

    clock_gettime(CLOCK_MONOTONIC_PRECISE, &start);

    for (i = 0; i < 10000000000 / interval; i++) {
        num_events = kevent(kq, &inqueue, 1, &outqueue, 1, NULL);
        if (num_events == -1) {
            fprintf(stderr, "kevent errno = %s", strerror(errno));
            exit(EXIT_FAILURE);
        } else if (outqueue.flags & EV_ERROR) {
            fprintf(stderr, "EV_ERROR: %s\n", strerror(outqueue.data));
            exit(EXIT_FAILURE);
        }
        if (num_events != 1)
            printf("num_events %d at i %d\n", num_events, i);
    }
    clock_gettime(CLOCK_MONOTONIC_PRECISE, &end);

    nsec = (end.tv_sec * 1000000000LL + end.tv_nsec) - 
        (start.tv_sec * 1000000000LL + start.tv_nsec);
    printf("nsec = %jd for %d loops of %d nsec\n",
         (intmax_t)nsec, i, interval);

    close(kq);
    return EXIT_SUCCESS;
}

-- Ian



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1481776532.1889.461.camel>