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