home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / Statistics.pm < prev    next >
Encoding:
Perl POD Document  |  2004-01-21  |  8.8 KB  |  292 lines

  1. # $Id: Statistics.pm,v 1.1 2004/01/21 17:27:01 rcaputo Exp $
  2.  
  3. # Data and methods to collect runtime statistics about POE, allowing
  4. # clients to look at how much work their POE server is performing.
  5. # None of this stuff will activate unless TRACE_STATISTICS or
  6. # TRACE_PROFILE are enabled.
  7.  
  8. package POE::Resources::Statistics;
  9.  
  10. use vars qw($VERSION);
  11. $VERSION = do {my@r=(q$Revision: 1.1 $=~/\d+/g);sprintf"%d."."%04d"x$#r,@r};
  12.  
  13. # We fold all this stuff back into POE::Kernel
  14. package POE::Kernel;
  15.  
  16. use strict;
  17.  
  18. # We keep a number of metrics (idle time, user time, etc).
  19. # Every tick (by default 30secs), we compute the rolling average
  20. # of those metrics. The rolling average is computed based on
  21. # the number of readings specified in $_stat_window_size.
  22.  
  23. my $_stat_metrics     = []; # the data itself
  24. my $_stat_interval    = 30; # how frequently we take readings
  25. my $_stat_window_size = 4;  # how many readings we average across
  26. my $_stat_wpos        = 0;  # where to currrently write metrics (circ. buffer)
  27. my $_stat_rpos        = 0;  # where to currrently write metrics (circ. buffer)
  28. my %average;
  29.  
  30. # This is for collecting event frequencies if TRACE_PROFILE is
  31. # enabled.
  32. my %profile;
  33.  
  34. sub _data_stat_initialize {
  35.     my ($self) = @_;
  36.     $self->_data_stat_reset;
  37.     $self->_data_ev_enqueue(
  38.       $self, $self, EN_STAT, ET_STAT, [ ],
  39.       __FILE__, __LINE__, time() + $_stat_interval
  40.     );
  41. }
  42.  
  43. sub _data_stat_finalize {
  44.     my ($self) = @_;
  45.     $self->_data_stat_tick();
  46.  
  47.     if (TRACE_STATISTICS) {
  48.       POE::Kernel::_warn(
  49.         '<pr> ,----- Observed Statistics ' , ('-' x 50), ",\n"
  50.       );
  51.       foreach (sort keys %average) {
  52.           next if /epoch/;
  53.           POE::Kernel::_warn(
  54.             sprintf "<pr> | %60.60s %9.1f  |\n", $_, $average{$_}
  55.           );
  56.       }
  57.  
  58.       unless (keys %average) {
  59.           POE::Kernel::_warn '<pr> `', ('-' x 73), "'\n";
  60.           return;
  61.       }
  62.  
  63.       # Division by zero sucks.
  64.       $average{blocked}     ||= 0;
  65.       $average{user_events} ||= 1;
  66.  
  67.       POE::Kernel::_warn(
  68.         '<pr> +----- Derived Statistics ', ('-' x 39), "+\n",
  69.         sprintf(
  70.           "<pr> | %60.60s %9.1f%% |\n",
  71.           'idle', 100 * $average{avg_idle_seconds} / $average{interval}
  72.         ),
  73.         sprintf(
  74.           "<pr> | %60.60s %9.1f%% |\n",
  75.           'user', 100 * $average{avg_user_seconds} / $average{interval}
  76.         ),
  77.         sprintf(
  78.           "<pr> | %60.60s %9.1f%% |\n",
  79.           'blocked', 100 * $average{avg_blocked} / $average{user_events}
  80.         ),
  81.         sprintf(
  82.           "<pr> | %60.60s %9.1f  |\n",
  83.           'user load', $average{avg_user_events} / $average{interval}
  84.         ),
  85.         '<pr> `', ('-' x 73), "'\n"
  86.       );
  87.     }
  88.  
  89.     if (TRACE_PROFILE) {
  90.       stat_show_profile();
  91.     }
  92. }
  93.  
  94. sub _data_stat_add {
  95.     my ($self, $key, $count) = @_;
  96.     $_stat_metrics->[$_stat_wpos] ||= {};
  97.     $_stat_metrics->[$_stat_wpos]->{$key} += $count;
  98. }
  99.  
  100. sub _data_stat_tick {
  101.     my ($self) = @_;
  102.  
  103.     my $pos = $_stat_rpos;
  104.     $_stat_wpos = ($_stat_wpos+1) % $_stat_window_size;
  105.     if ($_stat_wpos == $_stat_rpos) {
  106.     $_stat_rpos = ($_stat_rpos+1) % $_stat_window_size;
  107.     }
  108.  
  109.     my $count = 0;
  110.     %average = ();
  111.     my $epoch = 0;
  112.     while ($count < $_stat_window_size && $_stat_metrics->[$pos]->{epoch}) {
  113.      $epoch = $_stat_metrics->[$pos]->{epoch} unless $epoch;
  114.     while (my ($k,$v) = each %{$_stat_metrics->[$pos]}) {
  115.         next if $k eq 'epoch';
  116.         $average{$k} += $v;
  117.     }
  118.     $count++;
  119.     $pos = ($pos+1) % $_stat_window_size;
  120.     }
  121.  
  122.     if ($count) {
  123.         my $now = time();
  124.      map { $average{"avg_$_"} = $average{$_} / $count } keys %average;
  125.      $average{total_duration} = $now - $epoch;
  126.      $average{interval}       = ($now - $epoch) / $count;
  127.     }
  128.  
  129.     $self->_data_stat_reset;
  130.     $self->_data_ev_enqueue(
  131.       $self, $self, EN_STAT, ET_STAT, [ ],
  132.       __FILE__, __LINE__, time() + $_stat_interval
  133.     ) if $self->_data_ses_count() > 1;
  134. }
  135.  
  136. sub _data_stat_reset {
  137.     $_stat_metrics->[$_stat_wpos] = {
  138.       epoch => time,
  139.       idle_seconds => 0,
  140.       user_seconds => 0,
  141.       kern_seconds => 0,
  142.       blocked_seconds => 0,
  143.     };
  144. }
  145.  
  146. # Profile this event.
  147.  
  148. sub _stat_profile {
  149.   my ($self, $event) = @_;
  150.   $profile{$event}++;
  151. }
  152.  
  153. # Public routines...
  154.  
  155. sub stat_getdata {
  156.     return %average;
  157. }
  158.  
  159. sub stat_show_profile {
  160.   POE::Kernel::_warn('<pr> ,----- Event Profile ' , ('-' x 53), ",\n");
  161.   foreach (sort keys %profile) {
  162.     POE::Kernel::_warn(
  163.       sprintf "<pr> | %60.60s %9d  |\n", $_, $profile{$_}
  164.     );
  165.   }
  166.   POE::Kernel::_warn '<pr> `', ('-' x 73), "'\n";
  167. }
  168.  
  169. 1;
  170. __END__
  171.  
  172. =head1 NAME
  173.  
  174. POE::Resource::Statistics -- Runtime statistics for POE programs
  175.  
  176. =head1 SYNOPSIS
  177.  
  178.   my %stats = $poe_kernel->stat_getdata;
  179.   printf "Idle = %3.2f\n", 100*$stats{avg_idle_seconds}/$stats{interval};
  180.  
  181. =head1 DESCRIPTION
  182.  
  183. This module tracks runtime statistics for a POE program and provides
  184. accessors to them.  To enable this monitoring, the TRACE_STATISTICS
  185. flag must be true.  Otherwise no statistics will be gathered.
  186.  
  187. The statistics counters are totalled every 30 seconds and a rolling
  188. average is maintained for the last two minutes worth of data. At any
  189. time the data can be retrieved using the stat_getdata() method of the
  190. POE::Kernel. On conclusion of the program, the statistics will be
  191. printed out by the POE::Kernel.
  192.  
  193. The time() function is used to gather statistics over time.  If
  194. Time::HiRes is available, it will be used automatically.  Otherwise
  195. time is measured in whole seconds, and the resulting rounding errors
  196. will make the statistics useless.
  197.  
  198. Runtime statistics gathering was added to POE 0.28.  It is considered
  199. highly experimental.  Please be advised that the figures are quite
  200. likely wrong.  They may in fact be useless.  The reader is invited to
  201. investigate and improve the module's methods.
  202.  
  203. =head1 METRICS
  204.  
  205. The following fields are members of the hash returned by
  206. stat_getdata().
  207.  
  208. For each of the counters, there will a corresponding entry prefixed
  209. 'avg_' which is the rolling average of that counter.
  210.  
  211. =over 4
  212.  
  213. =item B<blocked>
  214.  
  215. The number of events (both user and kernel) which were delayed due to
  216. a user event running for too long. On conclusion of the program, POE
  217. will display the blocked count.  By comparing this value with
  218. B<user_events>.  This value should be as low as possible to ensure
  219. minimal latency.
  220.  
  221. In practice, this number is very close to (or even above)
  222. B<user_events>.  Events that are even the slightest bit late count as
  223. "blocked".  See B<blocked_seconds>.
  224.  
  225. TODO - Perhaps this should only count events that were dispatched more
  226. than 1/100 second or so late?  Even then, the hundredths add up in
  227. long running programs.
  228.  
  229. =item B<blocked_seconds>
  230.  
  231. The total number of seconds that handlers waited for other events or
  232. POE before being dispatched.  This value is not as useful as its
  233. average version, B<avg_blocked_seconds>, which tells you the average
  234. latency between an event's due time and its dispatch time.
  235.  
  236. =item B<idle_seconds>
  237.  
  238. The number of seconds which were spent doing nothing at all (typically
  239. waiting for a select/poll event or a timeout to trigger).
  240.  
  241. =item B<interval>
  242.  
  243. The average interval over which the counters are recorded. This will
  244. typically be 30 seconds, however it can be more if there are
  245. long-running user events which prevent the statistics gathering from
  246. running on time, and it may be less if the program finishes in under
  247. 30 seconds. Often the very last measurement taken before the program
  248. exits will use a duration less than 30 seconds and this will cause the
  249. average to be lower.
  250.  
  251. =item B<total_duration>
  252.  
  253. The counters are averaged over a 2 minute duration, but for the same
  254. reasons as described in the B<interval> section, this time may vary.
  255. This value contains the total time over which the average was
  256. calculated.
  257.  
  258. =item B<user_events>
  259.  
  260. The number of events which are performed for the user code. I.e. this
  261. does not include POE's own internal events such as polling for child
  262. processes. At program termination, a user_load value is computed
  263. showing the average number of user events which are running per
  264. second. A very active web server would have a high load value. The
  265. higher the user load, the more important it is that you have small
  266. B<blocked> and B<blocked_seconds> values.
  267.  
  268. =item B<user_seconds>
  269.  
  270. The time which was spent running user events. The user_seconds +
  271. idle_seconds will typically add up to total_duration. Any difference
  272. comes down to time spent in the POE kernel (which should be minimal)
  273. and rounding errors.
  274.  
  275. =back
  276.  
  277. =head1 SEE ALSO
  278.  
  279. See L<POE::Kernel>.
  280.  
  281. =head1 BUGS
  282.  
  283. Probably.
  284.  
  285. =head1 AUTHORS & COPYRIGHTS
  286.  
  287. Contributed by Nick Williams <Nick.Williams@morganstanley.com>.
  288.  
  289. Please see L<POE> for more information about authors and contributors.
  290.  
  291. =cut
  292.