#! /usr/bin/perl
#
# $Id:$
#
# Created By Tobi Oetiker <tobi@oetiker.ch>
# Date 2006-10-27
#
#makes programm work AFTER install

use lib qw( /usr/local/lib/perl );

print <<NOTE;

RRDtool Performance Tester
--------------------------
Runnion on $RRDs::VERSION;

RRDtool update performance is ultimately disk-bound. Since very little data
does actually get written to disk in a single update, the performance
is highly dependent on the cache situation in your machine.

This test tries to cater for this. It works like this:

1) Create 100 RRD files (and sync them to disk)

2) Update the 100 RRD file three times in a row.
   We run the Update several times to see the difference
   it makes in the cache.

3) Go back to 1)

NOTE

use strict;
use Time::HiRes qw(time);
use RRDs;
use IO::File;
use Time::HiRes qw( usleep );

sub create($$){
  my $file = shift;
  my $time = shift;
  my $start = time; #since we loaded HiRes
  RRDs::create  ( $file.".rrd", "-b$time", qw(
			-s300                        
		        DS:in:GAUGE:400:U:U
		        DS:out:GAUGE:400:U:U
		        RRA:AVERAGE:0.5:1:600
		        RRA:AVERAGE:0.5:6:600
		        RRA:MAX:0.5:6:600
		        RRA:AVERAGE:0.5:24:600
		        RRA:MAX:0.5:24:600
		        RRA:AVERAGE:0.5:144:600
		        RRA:MAX:0.5:144:600
		));
   my $total = time - $start;
   my $error =  RRDs::error;
   die $error if $error;
   return $total;
}

sub update($$){
  my $file = shift;
  my $time = shift;
  my $in = rand(1000);
  my $out = rand(1000);
  my $start = time;
  my $ret = RRDs::updatev($file.".rrd", $time.":$in:$out");
#  print join("",map {"  $_ " . $ret->{$_}."\n" } grep /AVERAGE.\[1\]/, sort keys %$ret)."\n** $time\n\n";
  # sync updates to disk immediately  
#  usleep(1) if (rand(3) <1 );
  my $total = time - $start;
  my $error =  RRDs::error;
  die $error if $error;
  return $total;
}

sub tune($){
  my $file = shift;
  my $start = time;
  RRDs::tune ($file.".rrd", "-a","in:U","-a","out:U","-d","in:GAUGE","-d","out:GAUGE");
  my $total = time - $start;
  my $error =  RRDs::error;
  die $error if $error;
  return $total;
}

sub infofetch($){
  my $file = shift;
  my $start = time;
  my $info = RRDs::info ($file.".rrd");
  my $error =  RRDs::error;
  die $error if $error;
  my $lasttime =  $info->{last_update} - $info->{last_update} % $info->{step};           
  my $fetch = RRDs::fetch ($file.".rrd",'AVERAGE','-s',$lasttime-1,'-e',$lasttime);
  my $total = time - $start;
  my $error =  RRDs::error;
  die $error if $error;
  return $total;
}

sub stddev ($$$){ #http://en.wikipedia.org/wiki/Standard_deviation
  my $sum = shift;
  my $squaresum = shift;
  my $count = shift;
  return sqrt( 1 / $count * ( $squaresum - $sum*$sum / $count ))
}

sub makerrds($$$$){
    my $count = shift;
    my $total = shift;
    my $list = shift;
    my $time = shift;
    my @files;
    for (1..$count){
        my $id = sprintf ("%07d",$total);
        $id =~ s/^(.)(.)(.)(.)(.)//;
        push @$list, "$1/$2/$3/$4/$5/$id";    
        -d "$1" or mkdir "$1";
        -d "$1/$2" or mkdir "$1/$2";
        -d "$1/$2/$3" or mkdir "$1/$2/$3";
        -d "$1/$2/$3/$4" or mkdir "$1/$2/$3/$4";
        -d "$1/$2/$3/$4/$5" or mkdir "$1/$2/$3/$4/$5";
	push @files, $list->[$total];
        create $list->[$total++],$time-2;
	print STDERR ".";
    }
   for (@files){ 
	my $fd = new IO::File("$_.rrd","r");
	if (defined $fd) {
	    $fd->sync;
	    $fd->close;
        } else {
            warn "failed to sync $_\n";
        }        
    }
    return $count;
}
    
    
sub main (){
    mkdir "db-$$" or die $!;
    chdir "db-$$";

    my $step = 100000; # number of rrds to creat for every round
    
    my @path;
    my $time=int(time);

    my $tracksize = 0;
    my $uppntr = 0;

    
    my %squaresum = ( cr => 0, up => 0 );
    my %sum = ( cr => 0, up => 0 );
    my %count =( cr => 0, up => 0 );

    my $printtime = time;
    while (1) {
        # enhance the track
  	    $time += 300;
        $tracksize += makerrds $step,$tracksize,\@path,$time;            
        # run benchmark
        for (0..10){
      	    $time += 300;
            my $count = 0;
            my $sum = 0;
            my $squaresum = 0;
            for (my $i = 0; $i<$tracksize;$i ++){
               my $elapsed = update($path[$i],$time); 
               $sum += $elapsed;
               $squaresum += $elapsed**2;
               $count++;
            };
#            for (my $i = 0; $i<$tracksize;$i ++){
#    	       my $fh = new IO::File "$path[$i].rrd","r";
#	       if (defined $fh) {
#	           $fh->sync;
#	           $fh->close;
#                } else {
#    	           warn "failed to sync $path[$i]\n";
#	        }       
#            }
            my $ups = $count/$sum;
            my $sdv = stddev($sum,$squaresum,$count);
            printf STDERR "%4d %6.0f Up/s (%6.5f sdv)\n",$count,$ups,$sdv;
        }
	print STDERR "\n";
	exit ;
    }
}

main;
