Algorithm-EventsPerSecond
view release on metacpan or search on metacpan
t/lib/AEPS_TestSuite.pm view on Meta::CPAN
$meter->mark(3)->mark(4);
is( $meter->count, 7, 'window of 1 counts the current second' );
advance(1);
is( $meter->count, 0, 'window of 1 expires after one second' );
}
#
# rate
#
{
my $meter = Algorithm::EventsPerSecond->new( window => 10 );
is( $meter->rate, 0, 'rate is 0 when no time has elapsed' );
$meter->mark(10);
advance(5);
is( $meter->rate, 2, 'rate uses elapsed lifetime while younger than the window' );
advance(15); # lifetime 20s > window; only recent events count
$meter->mark(30);
is( $meter->rate, 3, 'rate averages over the full window once mature' );
}
#
# reset
#
{
my $meter = Algorithm::EventsPerSecond->new( window => 10 );
$meter->mark(4);
advance(2);
$meter->mark(6);
is( $meter->reset, $meter, 'reset returns the meter object' );
is( $meter->count, 0, 'count is 0 after reset' );
is( $meter->total, 0, 'total is 0 after reset' );
is( $meter->rate, 0, 'rate is 0 after reset' );
advance(4);
$meter->mark(8);
is( $meter->count, 8, 'meter is usable after reset' );
is( $meter->rate, 2, 'rate clock restarts at reset' );
}
#
# clock stepping backwards (an NTP step): must not crash, must not
# produce negative readings, and must count correctly once the
# clock recovers
#
{
my $meter = Algorithm::EventsPerSecond->new( window => 5 );
$meter->mark(3);
advance(-11); # -11 is not a multiple of the window, so this
$meter->mark(2); # mark lands in a different bucket than the one above
ok( $meter->count >= 0, 'count is non-negative after the clock steps back' );
is( $meter->total, 5, 'total is unaffected by the clock stepping back' );
is( $meter->rate, 0, 'rate reads 0 while the clock is behind the start time' );
advance(13); # two seconds past where the clock originally was
$meter->mark(4);
is( $meter->count, 7, 'only in-window events counted once the clock recovers' );
is( $meter->total, 9, 'total tracks marks made at every clock position' );
advance(10);
is( $meter->count, 0, 'everything ages out normally after recovery' );
is( $meter->total, 9, 'total survives the aging out' );
}
#
# randomized cross-check against an independent reference model.
# Window sizes chosen to exercise the SIMD main loop, its scalar
# tail, and tail-only runs (AVX2 does 4 buckets a step, SSE4.2 2);
# 257 and 1024 add long main-loop runs on odd and even boundaries.
#
for my $window ( 1, 2, 3, 5, 8, 16, 33, 64, 257, 1024 ) {
my $meter = Algorithm::EventsPerSecond->new( window => $window );
my %events; # second => count, the reference model
my $total = 0;
my $bad = 0;
for ( 1 .. 200 ) {
advance( _rnd(4) ); # 0-3 seconds pass
my $n = _rnd(5); # 0-4 events
if ($n) {
$meter->mark($n);
$events{$fake_time} += $n;
$total += $n;
}
my $oldest = $fake_time - $window + 1;
my $expected = 0;
for my $sec ( keys %events ) {
$expected += $events{$sec} if $sec >= $oldest;
}
$bad++ if $meter->count != $expected;
$bad++ if $meter->total != $total;
} ## end for ( 1 .. 200 )
is( $bad, 0, "randomized marks match reference model (window $window)" );
} ## end for my $window ( 1, 2, 3, 5, 8, 16, 33, 64,...)
return;
} ## end sub run_suite
1;
( run in 1.493 second using v1.01-cache-2.11-cpan-7fcb06a456a )