Greetings, zapoi

I tried serial and parallel demonstrations that doesn't involve a busy CPU loop while waiting for the next time period. The resolution is near 1 millisecond. The monotonic clock is used if available.

Serial code:

use strict; use warnings; package Interval { use Time::HiRes; use constant HAS_CLOCK_MONOTONIC => eval { # use monotonic clock if available Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ); 1; }; sub new { my ($class, $delay) = @_; return bless [ $delay, undef ], $class; } sub yield { my ($self) = @_; my ($delay, $lapse) = @{ $self }; my $time = HAS_CLOCK_MONOTONIC ? Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ) : Time::HiRes::time(); if (!$delay || !defined $lapse) { $lapse = $time; } elsif ($lapse + $delay - $time < 0) { $lapse += int(abs($time - $lapse) / $delay + 0.5) * $delay; } $self->[1] = ($lapse += $delay); Time::HiRes::usleep(($lapse - $time) * 1e6); return; } } use Time::HiRes qw{ sleep time }; my $interval = Interval->new(0.1); my $last_value = 0; sub func { my ($last_value, $seq) = @_; printf "%0.3f $last_value $seq\n", time; sleep 0.1 if $seq == 3; # simulate extra time return $seq; } for my $seq (1..20) { # delay until the next time period $interval->yield; my $value = func($last_value, $seq); $last_value = $value; } __END__ 1597215036.077 0 1 1597215036.177 1 2 1597215036.277 2 3 # 0.1 gap between sequence 3 and 4 due to 3 taking +extra time 1597215036.477 3 4 1597215036.577 4 5 1597215036.677 5 6 1597215036.777 6 7 1597215036.877 7 8 1597215036.978 8 9 1597215037.078 9 10 1597215037.177 10 11 1597215037.277 11 12 1597215037.377 12 13 1597215037.476 13 14 1597215037.578 14 15 1597215037.677 15 16 1597215037.777 16 17 1597215037.878 17 18 1597215037.977 18 19 1597215038.077 19 20

Parallel code:

MCE::Relay makes it possible to obtain the value for the last execution. There is MCE->yield but was not able to use successfully with MCE::relay due to yield being computed by worker ID and relay by chunk ID internally. I will resolve this in the next MCE release. The MCE interval option (not shown here) and MCE->yield is how one throttles workers.

Here, the Interval class is updated to support time intervals among many workers (i.e. $lapse is sent and received using a channel).

use strict; use warnings; package Interval { use Time::HiRes; use MCE::Channel; use constant HAS_CLOCK_MONOTONIC => eval { # use monotonic clock if available Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ); 1; }; sub new { my ($class, $delay) = @_; my $chnl = MCE::Channel->new; $chnl->send( undef ); return bless [ $delay, $chnl ], $class; } sub yield { my ($delay, $chnl) = @{ $_[0] }; my $lapse = $chnl->recv; my $time = HAS_CLOCK_MONOTONIC ? Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ) : Time::HiRes::time(); if (!$delay || !defined $lapse) { $lapse = $time; } elsif ($lapse + $delay - $time < 0) { $lapse += int(abs($time - $lapse) / $delay + 0.5) * $delay; } $chnl->send($lapse += $delay); Time::HiRes::usleep(($lapse - $time) * 1e6); return; } } use MCE; use Time::HiRes qw{ sleep time }; my $interval = Interval->new(0.1); sub func { my ($last_value, $seq) = @_; printf "%0.3f $last_value $seq\n", time; sleep 0.1 if $seq == 3; # simulate extra time return $seq; } MCE->new( max_workers => 4, chunk_size => 1, sequence => [ 1, 20 ], init_relay => 0, user_func => sub { my $seq = $_; # wait for data from the last execution # this value will be 0 for the worker processing MCE->chunk_id 1 my $last_value = MCE->relay_recv; # delay until the next time period $interval->yield; my $value = func($last_value, $seq); # relay data to the next worker MCE::relay { $_ = $value }; } )->run; __END__ 1597215117.883 0 1 1597215117.983 1 2 1597215118.083 2 3 # 0.1 gap between sequence 3 and 4 due to 3 taking +extra time 1597215118.283 3 4 1597215118.382 4 5 1597215118.482 5 6 1597215118.583 6 7 1597215118.683 7 8 1597215118.783 8 9 1597215118.882 9 10 1597215118.983 10 11 1597215119.082 11 12 1597215119.182 12 13 1597215119.283 13 14 1597215119.383 14 15 1597215119.483 15 16 1597215119.583 16 17 1597215119.683 17 18 1597215119.782 18 19 1597215119.883 19 20

Regards, Mario


In reply to Re: Call function no more than every 0.1 seconds by marioroy
in thread Call function no more than every 0.1 seconds by zapoi

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.