Algorithm-EventsPerSecond

 view release on metacpan or  search on metacpan

lib/Algorithm/EventsPerSecond.pm  view on Meta::CPAN

		if ( eval { require Algorithm::EventsPerSecond::XS; 1 } ) {
			$BACKEND = 'XS';
		}
	}
}

=head1 SYNOPSIS

    use Algorithm::EventsPerSecond;

    my $meter = Algorithm::EventsPerSecond->new( window => 10 );  # 10-second window

    while (my $event = get_next_event()) {
        $meter->mark;                 # record one event
        # $meter->mark(5);            # or record several at once

        printf "current rate: %.2f events/sec\n", $meter->rate;
    }

    print "events seen in window: ", $meter->count, "\n";
    print "lifetime total:        ", $meter->total, "\n";


=head1 DESCRIPTION

Algorithm::EventsPerSecond keeps per-second counts in a fixed-size ring buffer and
reports the average event rate over the most recent N seconds (the
"window"). Memory use is constant regardless of event volume, and both
C<mark> and C<rate> are O(1) averaged out over time.

=head1 METHODS

=head2 new( window => $seconds )

Construct a meter. C<window> is the length of the averaging window in
seconds and defaults to 60.

=cut

sub new {
	my ( $class, %args ) = @_;

	my $window = $args{window} // 60;
	die "window must be a positive integer" unless $window =~ /^\d+$/ && $window > 0;

	my $self = {
		window  => $window,
		total   => 0,         # lifetime event count
		started => time(),
	};

	if ( $BACKEND eq 'XS' ) {
		# packed int64_t ring buffers, scanned in C
		$self->{buckets} = "\0" x ( $window * 8 );
		$self->{stamps}  = "\0" x ( $window * 8 );
	} else {
		$self->{buckets} = [ (0) x $window ];    # counts, indexed by (epoch_sec % window)
		$self->{stamps}  = [ (0) x $window ];    # epoch second each bucket belongs to
	}

	return bless $self, $class;
} ## end sub new

# Internal, PP backend: get the bucket for the current second, clearing it if stale.
sub _bucket_index {
	my ( $self, $now_sec ) = @_;
	my $i = $now_sec % $self->{window};
	if ( $self->{stamps}[$i] != $now_sec ) {
		$self->{buckets}[$i] = 0;
		$self->{stamps}[$i]  = $now_sec;
	}
	return $i;
}

=head2 mark( [$count] )

Record one event, or C<$count> events. C<$count> must be a
non-negative integer; zero is a no-op. Anything else dies. Returns the
meter object, so calls can be chained.

=cut

sub _mark_pp {
	my ( $self, $count ) = @_;
	if ( defined $count ) {
		die "count must be a non-negative integer" unless $count =~ /^\d+$/;
	} else {
		$count = 1;
	}

	my $now_sec = int( time() );
	my $i       = $self->_bucket_index($now_sec);

	$self->{buckets}[$i] += $count;
	$self->{total} += $count;

	return $self;
} ## end sub _mark_pp

sub _mark_xs {
	my ( $self, $count ) = @_;
	if ( defined $count ) {
		die "count must be a non-negative integer" unless $count =~ /^\d+$/;
	} else {
		$count = 1;
	}

	Algorithm::EventsPerSecond::XS::_xs_mark( $self->{buckets}, $self->{stamps},
		$self->{window}, int( time() ), $count, );
	$self->{total} += $count;

	return $self;
} ## end sub _mark_xs

=head2 count

Number of events recorded within the current window.

=cut

sub _count_pp {



( run in 0.673 second using v1.01-cache-2.11-cpan-0b5f733616e )