nothingmuch has asked for the wisdom of the Perl Monks concerning the following question:

I was wondering how one may perform a given action every x seconds (or other, perhaps smaller) unit of time. My most logical answer is using Time::HiRes to sleep for x milliseconds, minus the ones the action took us to perform. This involves getting the current time a lot, to see how late we are, and all that.

Operations are possibly blocking, so they may take quite a bit of real time. Doing that seems ridiculusly wasteful, because there are so many system calls. But there is a reason - accuracy can fluxuate, but shouldn't accumilate fluxuations - simply sleeping for a constant time unit is not an option. In other words i'd like the script to correct it's timed interval so that it's somewhat in sync with the system clock.

My idea was something like this (say we want every second, maintaining relative accuracy for periods of a year or so):
my $x = 10; # the number of iterations to do between each sync my $int = 10; # the desired interval between actions my $y = 0; # another iteration limit, for catching up foo: { my $time = time(); for (my $i = 0; $i <= $y; $i++){ # catch up with real time from th +e last loop action(); # perform the action } for (my $i = 0; $i < $x; $i++){ action(); sleep $int; } my $ntime = time(); $y = int ( ($ntime - $time - $x * $int) / $int ); # calculate how +many we missed, and round down redo foo; }
Any suggestions?

-nuffin
zz zZ Z Z #!perl

Replies are listed 'Best First'.
Re: Accurately timed reccuring events
by Jenda (Abbot) on Mar 22, 2003 at 17:46 UTC

    It seems that Time::Hires::setitimer() might be what you are after. That should send you the signals in even intervals. I can't test it out and suggest more details. The function doesn't seem to work under Win32 (Win2kServer, ActivePerl 5.8 build 805).

    Jenda
    Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
       -- Rick Osborne

    Edit by castaway: Closed small tag in signature

      So i guess that's an alarm_every system call, ;-)...

      -nuffin
      zz zZ Z Z #!perl
Re: Accurately timed reccuring events
by mojotoad (Monsignor) on Mar 22, 2003 at 19:28 UTC
    If it's that critical, you might be on the wrong platform. Perhaps you should investigate a Real Time Operating System, such as RTLinux or QNX.

    No matter how fancy you get with your scheduling, there's no guarantee with an ordinary OS that your event will not be preempted by some other system event.

    Matt

      Either you use a module with alarm functionality (which polls the time for you) or you poll the clock yourself...then DO_EVENTS so the system can do stuff...then poll again to see if current_time - last_event_time >= period. If you want this code to be "fairly" reliable in timing, make it a service with the win32 service module. But even a service can be delayed for a long time, eg system decides to increase size of swap file etc. If you want guaranteed n calls to periodic_procedure for n seconds with little or no latency, then Real Time OS to start with is your only option in my opinion. Chris
Re: Accurately timed reccuring events
by benn (Vicar) on Mar 22, 2003 at 17:34 UTC
    You could maybe use threads...you might have to deduct some constant from your sleep time to account for the extra 'startup' though.
    foreach(1..$number_of_iterations) { my $t = threads->new(\&action); sleep $whatever; $t->join(); }
    Cheers.
      Allthough the concept of the practicality is correct, i think the practicality hasn't changed (at least not until copy-on-write, as far as i know), nor has the concept - it's still liable to fluxuate on a very long term basis... But at least this can be treated more of a O(1) than O(N) operation. Sadly my needs are probably O(1) to start with... This should help with blocking tho, which is a plus.

      If the action is costly enough i reckon i'd wrap it in a thread, but that's probably only if the task might actually take longer than the sleep interval... i dunno.

      I haven't thought of the constant time before, although it's a possibility. It's fragile points are a busy time sharing system (where the spawning time may vary wildly), and calculation. An alternative is to start with zero as a the deduced value, and then to loop some like in my code above. We cache the startup time aswell, and know (via multiplication) the time we spent sleeping. We can then calculate how much time was not slept, based on the real time which has passed, and that value, divided by the number of times we have performed the action since our 'startup' can be taken off the sleep intervals. If it's too much the value can ofcourse become negative, and thus add to the accuracy. If the action is short enough we can just omit the thread spawning part.

      Thanks dearly for that constant concept, it really made me think... =)

      I wish there was an 'alarm_every' system call ;-)


      Update: i have written some Time::HiRes dependant code, which doesn't use signals. It swings around the desired frequency with an accuracy of around 1 millisecond on my machine, correcting itself as needed. Here is the source code:
Re: Accurately timed reccuring events
by bart (Canon) on Mar 25, 2003 at 13:16 UTC
    How about usleep(), also from Time::HiRes? Or Time::HiRes::sleep()?

    An old traditional way to do this is the 4-argument select(), with the first 3 arguments set to undef. See the bottom of the perlfunc entry for select.