#!/usr/bin/perl -T

# examples/benchmark.pl
#  Compare the ISAAC RNG implementations between each other, and between
#  other algorithms.
#
# $Id: benchmark.pl 6598 2009-04-24 01:09:11Z FREQUENCY@cpan.org $
#
# By Jonathan Yu <frequency@cpan.org>, 2009. All rights reversed.
#
# This package and its contents are released by the author into the
# Public Domain, to the full extent permissible by law. For additional
# information, please see the included `LICENSE' file.

use strict;
use warnings;

eval {
  require Benchmark::ProgressBar;
  Benchmark::ProgressBar->import('cmpthese');
};
if ($@) {
  require Benchmark;
  Benchmark->import('cmpthese');
}

=head1 NAME

benchmark.pl - Test performance of various random number generators

=head1 VERSION

Version 1.0 ($Id: benchmark.pl 6598 2009-04-24 01:09:11Z FREQUENCY@cpan.org $)

=cut

use version; our $VERSION = qv('1.0');

my $LOOPS    = 10_000; # Number of sequences to generate
my $NUMTESTS = 2_500; # Numbers to generate each test
my %TESTING; # modules we're testing

=head1 SYNOPSIS

Usage: benchmark.pl

This script will automatically detect available PRNG algorithms and compare
them using the Benchmark module. It will keep Pure Perl and C/XS modules
separate, so as to compare apples to apples.

=head1 DESCRIPTION

This script currently knows about the following modules, and will compare
them if they are installed.

This module will first compile all of the modules; loading time is also
benchmarked and compared, but separately from the rest of the operation.
For object oriented random number algorithms, the constructor is called
during the benchmark loop, so overhead due to initialization is fairly
counted too.

It will get 5 million integers from each, so as to provide a good sample
size. You can tune this, but you'll have to edit the file.

=head2 ALGORITHMS

=over

=item * Math::Random::ISAAC::PP (Perl)

=item * Math::Random::ISAAC::XS (XS/C)

=item * Math::Random::MT (XS/C)

=item * Math::Random::MT::Perl (Perl)

=item * Math::Random::TT800 (XS/C)

=item * Math::Random::random_uniform_integer (XS/C)

=item * Perl core rand() function

=back

=cut

print "Loading modules, please wait...\n";
load('Math::Random');
load('Math::Random::ISAAC::XS');
load('Math::Random::ISAAC::PP');
load('Math::Random::TT800');
load('Math::Random::MT');
load('Math::Random::MT::Perl');

print 'Setting up C/XS number generators... ';
my $seed = time;
my $code = {};

# Set up all of the modules we've just loaded
$code->{'Core'} = sub {
  my $var;
  srand($seed);
  for (1..$NUMTESTS) {
    $var = rand();
  }
};

if ($TESTING{'Math::Random'}) {
  $code->{'Math::Random'} = sub {
    my $var;
    Math::Random::random_set_seed($seed, $seed);
    for (1..$NUMTESTS) {
      $var = Math::Random::random_uniform();
    }
  };
}

if ($TESTING{'Math::Random::ISAAC::XS'}) {
  $code->{'ISAAC::XS'} = sub {
    my $rng = Math::Random::ISAAC::XS->new($seed);
    my $var;
    for (1..$NUMTESTS) {
      $var = $rng->irand();
    }
  };
}

if ($TESTING{'Math::Random::ISAAC::PP'}) {
  $code->{'ISAAC::PP'} = sub {
    my $rng = Math::Random::ISAAC::PP->new($seed);
    my $var;
    for (1..$NUMTESTS) {
      $var = $rng->irand();
    }
  };
}

if ($TESTING{'Math::Random::TT800'}) {
  $code->{'TT800'} = sub {
    my $rng = Math::Random::TT800->new($seed);
    my $var;
    for (1..$NUMTESTS) {
      $var = $rng->next_int();
    }
  };
}

if ($TESTING{'Math::Random::MT'}) {
  $code->{'MT'} = sub {
    my $rng = Math::Random::MT->new($seed);
    my $var;
    for (1..$NUMTESTS) {
      $var = $rng->rand();
    }
  };
}

if ($TESTING{'Math::Random::MT::Perl'}) {
  $code->{'MT::Perl'} = sub {
    my $rng = Math::Random::MT::Perl->new($seed);
    my $var;
    for (1..$NUMTESTS) {
      $var = $rng->rand();
    }
  };
}

print "done.\n";

print "Running comparisons (this might take a while)...\n\n";

cmpthese($LOOPS, $code);

sub load {
  my ($module) = @_;

  print '  ' . $module . '... ';
  eval 'use ' . $module;
  if ($@) {
    print 'not installed.';
    $TESTING{$module} = 0;
  }
  else {
    print 'done.';
    $TESTING{$module} = 1;
  }
  print "\n";
  return;
}

=head1 AUTHOR

Jonathan Yu E<lt>frequency@cpan.orgE<gt>

=head1 SUPPORT

For support details, please look at C<perldoc Math::Random::ISAAC> and
use the corresponding support methods.

=head1 LICENSE

Copyleft (C) 2009 by Jonathan Yu <frequency@cpan.org>

This script is distributed with the C<Math::Random::ISAAC> package and
related packages to provide a simple demonstration of functionality. It is
hereby released by its author into the public domain.

=head1 SEE ALSO

L<Math::Random::ISAAC::PP>,
L<Math::Random::ISAAC::XS>,
L<Math::Random::MT>,
L<Math::Random::MT::Perl>,
L<Math::Random::TT800>,
L<Math::Random::random_uniform_integer>,

=cut
