AnyEvent::Monitor::CPU - monitors your process CPU usage, with high/low watermark triggers


AnyEvent-Monitor-CPU documentation Contained in the AnyEvent-Monitor-CPU distribution.

Index


Code Index:

NAME

Top

AnyEvent::Monitor::CPU - monitors your process CPU usage, with high/low watermark triggers

VERSION

Top

version 0.3

SYNOPSIS

Top

    use AnyEvent::Monitor::CPU qw( monitor_cpu );

    my $monitor = monitor_cpu cb => sub {
        my ($self, $on_off_flag) = @_;

        # look at $on_off_flag
        #   * 1: below the low watermak - you can increase your CPU usage
        #   * 0: above the high watermark - reduce your CPU usage;
      }
    ;

    ## Or...

    use AnyEvent::Monitor::CPU;

    my $monitor = AnyEvent::Monitor::CPU->new(
      cb => sub { ... }
    );

    ## other goodies
    my $last_measured_usage = $monitor->usage;
    my $have_spare_cpu = $monitor->is_low;
    my $we_are_overloaded = $monitor->is_high;

    ## stats
    use Data::Dump qw(pp);
    my $stats = $monitor->stats;
    print pp($stats);
    # {
    #   usage_count => 5,
    #   usage_sum   => 3.344552,
    #   usage_avg   => 0.668910,
    #   usage       => 0.540231,
    # }

    $monitor->reset_stats;
    print pp($monitor->stats);
    # {
    #   usage_count => 0,
    #   usage_sum   => 0,
    #   usage       => 0.481203,
    # }

    ## monitor stop/start control
    $monitor->stop;
    $monitor->start;
    $monitor->is_running;




DESCRIPTION

Top

This module gives you a CPU monitor with high/low threseholds and triggers.

On a regular basis, it will check the CPU usage of the current process. If the usage is above your designated upper limit for more than a number of samples, it will trigger the provided callback.

If the CPU usage lowers below the provided lower limit for more than a number of samples, it will trigger the callback again.

See the constructor "new()" documentation for all the parameters that you can set, and their default values.

For each transition (above upper limit, below lower limit), the callback will be called only once.

All load values are between 0 and 1, between a idle processor (0) and a full blown fried eggs machine (1).

You can inspect the current state of the monitor. You can check the last usage that was sampled, and the average usage over the last samples.

An API is also provided to stop and restart the monitor.

PERFORMANCE

Top

With the default parameters, the overhead of the CPU monitor is less than 0.05%, as measured on both of my development machines (a MacBook laptop with a 2.16Ghz Intel Core Duo, and a PC with a 2.66Ghz Intel Quad Core).

Please note that if you run your code under Devel::Cover the average loads will most likelly be outside the limits.

METHODS

Top

new()

    $cpu = AnyEvent::Monitor::CPU->new( cb => sub {}, ... );
    $cpu = AnyEvent::Monitor::CPU->new({ cb => sub {}, ... });

Creates a new AnyEvent::Monitor::CPU object and start the polling process.

The following parameters are accepted:

cb
    cb => sub {
      my ($monitor, $high_low_flag) = @_;

      say $high_low_flag? "I'm bored..." : "I'm high as a kite!";
    },

The callback to be used when the CPU usage rises above or lowers below the defined thresholds.

This parameter is required and it should be a coderef.

The callback will be called with two parameters:

$monitor

The monitor object.

$high_low_flag

Flag with the current state of the CPU usage. The possible values are:

0

The CPU is over the defined high limit.

1

The CPU is under the defined low limit.

Your callback should tune the application to increase or decrease the CPU usage based on the $high_low_flag value.

interval
    interval => .1, ## sample 10 times per second

The sample interval. Use fractional values for sub-second intervals.

The default value is .25, so it will sample the CPU usage 4 times per second.

high
    high => .40,  ## set the upper limit to 40%

Defines the upper limit for the CPU usage.

The default value is .95.

low
    low => .20,  ## set the lower limit to 20%

Defines the lower limit for the CPU usage.

The default value is .80.

high_samples
    ## Only trigger the cb after 4 consecutive samples above the
    ## high limit
    high_samples => 4,

Set the number of samples above the high limit that we require to trigger the callback.

The default value is 2.

low_samples
    ## Only trigger the cb after 2 consecutive samples under the
    ## low limit
    low_samples => 4,

Set the number of samples under the low limit that we require to trigger the callback.

The default value is 2.

monitor_cpu()

    my $monitor = monitor_cpu cb => sub {}, interval => .1;

A shortcut to AnyEvent::Monitor::CPU->new(@args). All parameters are passed along to the "new()" constructor.

The monitor_cpu() function is not imported by default. You must ask for it.

usage()

    $usage = $monitor->usage()

Returns the last sampled CPU usage.

The value returned is between 0 and 1.

is_high()

    if ($monitor->is_high()) {
      say "Your eggs will be ready in a minute";
    }

Returns true if the CPU usage is over the defined limits.

is_low()

    if ($monitor->is_low()) {
      say "Its chilly in here, wanna generate some heat?";
    }

Returns true if the CPU usage is below the defined limits.

stats()

    my $stats = $monitor->stats;
    my $count = $stats->{usage_count};
    say "Average usage was $stats->{usage_avg} over the last $count samples"
      if $count;

Returns a hashref with statistics. The following keys are available:

usage

The last usage sample taken. Its the same as $monitor->usage.

usage_count

The number of samples taken since the last \"reset_stats()".

usage_sum

The sum of the samples taken since the last \"reset_stats()".

usage_avg

The average usage since the last \"reset_stats()".

This value is only available if usage_count is non-zero.

reset_stats()

    $monitor->reset_stats();

Clears the stats.

stop()

    $monitor->stop();

Stops the polling process for the CPU monitor.

start()

    $monitor->start();

Starts the polling process for the CPU monitor.

is_running()

    if ($monitor->is_running()) {
      say "Big brother is watching, play it cool";
    }
    else {
      say "Bring on the bacon and eggs, lets make breakfast!";
    }

Returns true if the monitor is polling the CPU usage.

SEE ALSO

Top

Proc::CPUUsage.

AUTHOR

Top

Pedro Melo, <melo at cpan.org>

COPYRIGHT & LICENSE

Top


AnyEvent-Monitor-CPU documentation Contained in the AnyEvent-Monitor-CPU distribution.

package AnyEvent::Monitor::CPU;
our $VERSION = '0.3';



use common::sense;
use AnyEvent;
use Proc::CPUUsage;
use Carp qw( croak );
use parent qw( Exporter );

@AnyEvent::Monitor::CPU::EXPORT_OK = ('monitor_cpu');

## Shortcut, optional import
sub monitor_cpu { return __PACKAGE__->new(@_) }


sub new {
  my $class = shift;
  my %args = @_ == 1 ? %{$_[0]} : @_;

  my $self = bless {
    cb => delete $args{cb},

    interval => delete $args{interval} || .25,

    high         => delete $args{high}         || .95,
    low          => delete $args{low}          || .80,
    high_samples => delete $args{high_samples} || 2,
    low_samples  => delete $args{low_samples}  || 2,
    cur_high_samples => 0,
    cur_low_samples  => 0,

    cpu   => Proc::CPUUsage->new,
    usage => undef,
    state => 1,
  }, $class;

  croak("Required parameter 'cb' not found, ") unless $self->{cb};
  croak("Parameter 'cb' must be a coderef, ") unless ref $self->{cb} eq 'CODE';

  $self->start;

  return $self;
}

sub start {
  my $self = shift;

  $self->{timer} = AnyEvent->timer(
    after    => $self->{interval},
    interval => $self->{interval},
    cb       => sub { $self->_check_cpu },
  );

  $self->{usage} = $self->{cpu}->usage;
  $self->reset_stats;  

  return;
}

sub stop       { delete $_[0]->{timer} }
sub is_running { $_[0]->{timer} }

sub usage   { return $_[0]->{usage} }
sub is_low  { return $_[0]->{state} == 1 }
sub is_high { return $_[0]->{state} == 0 }

sub reset_stats {
  my ($self) = @_;
  
  $self->{usage_sum} = 0;
  $self->{usage_count} = 0;
}

sub stats {
  my ($self) = @_;
  my %stats;
  
  my ($count, $sum);
  if ($count = $self->{usage_count}) {
    $sum = $self->{usage_sum};
    $stats{usage_avg} = $sum/$count;
  }
  $stats{usage_count} = $count;
  $stats{usage_sum}   = $sum;
  $stats{usage}       = $self->{usage};

  return \%stats;
}

sub _check_cpu {
  my $self = $_[0];
  my $chs  = $self->{current_high_samples};
  my $cls  = $self->{current_low_samples};

  my $usage = $self->{usage} = $self->{cpu}->usage;
  if    ($usage > $self->{high}) { $chs++; $cls = 0 }
  elsif ($usage < $self->{low})  { $cls++; $chs = 0 }
  else {
    $chs-- if $chs;
    $cls-- if $cls;
  }
  $self->{usage_sum} += $usage;
  $self->{usage_count}++;

  my $hs      = $self->{high_samples};
  my $ls      = $self->{low_samples};
  my $state   = $self->{state};
  my $trigger = 0;
  if ($chs >= $hs) {
    $chs = $hs;
    if ($state) {
      $state   = 0;
      $trigger = 1;
    }
  }
  elsif ($cls >= $ls) {
    $cls = $ls;
    if (!$state) {
      $state   = 1;
      $trigger = 1;
    }
  }

  $self->{state}                = $state;
  $self->{current_high_samples} = $chs;
  $self->{current_low_samples}  = $cls;

  $self->{cb}->($self, $state) if $trigger;
}


1;

__END__