diff --git a/bench.pl b/bench.pl new file mode 100755 index 0000000..75f1254 --- /dev/null +++ b/bench.pl @@ -0,0 +1,169 @@ +#!/usr/bin/env perl + +use lib qw(bench/lib); + +use strict; +use warnings; +use Data::Dumper; + +$ENV{EVENT_SEND_COUNT} = 500000 unless exists $ENV{EVENT_SEND_COUNT}; +$ENV{MIN_TEST_TIME} = 60 unless exists $ENV{MIN_TEST_TIME}; +$ENV{BENCH_DIR} = 'bench' unless exists $ENV{BENCH_DIR}; + +logger("Configuration: $ENV{EVENT_SEND_COUNT} events and $ENV{MIN_TEST_TIME} seconds minimum test time\n"); + +my @results = doTests(); +my $error = shift(@results); +doLog(@results); +exit($error); + +exit(0); + +sub logger { + print STDERR @_; +} + +sub genFiles { + my ($dir) = @_; + my $dh; + my @paths; + + die "$dir is not a directory or is not accessable" unless -d $dir; + + die "Could not opendir($dir): $!" unless opendir($dh, $dir); + + foreach(readdir($dh)) { + next if m/^\./; + next if -d $_; + + push(@paths, "$dir/$_"); + } + + die "Could not closedir($dir): $!" unless closedir($dh); + + return @paths; +} + +sub genTests { + my @files; + my @buf; + + if (scalar(@ARGV) > 0) { + @files = @ARGV; + } else { + logger("Loading benchmarks from $ENV{BENCH_DIR}/... "); + @files = genFiles($ENV{BENCH_DIR}); + logger("found ", scalar(@files), " files\n"); + } + + foreach(@files) { + my %test = ( file => $_ ); + + logger("loading $_... "); + + $test{cb} = require $_; + + logger("done\n"); + + push(@buf, \%test); + } + + return @buf; +} + +sub randomInt { + return int(rand(10)); +} + +sub genData { + my @buf = @_; + + logger("Generating test data... "); + + foreach(1 .. $ENV{EVENT_SEND_COUNT}) { + push(@buf, [ randomInt(), randomInt() ]); + } + + logger("done\n"); + return @buf; +} + +sub sumData { + my $sum = 0; + + foreach(@_) { + $sum += $_->[0] + $_->[1]; + } + + return $sum; +} + +sub doTests { + my @tests = genTests(); + my @testData = genData(); + my $sum = sumData(@testData); + my $error = 0; + + foreach(@tests) { + my $seconds = 0; + my $eventCount = 0; + my $iterations = 0; + + logger("Measuring $_->{file}... "); + + while(1) { + my ($results, $bench); + + $iterations++; + + $results = $_->{cb}(@testData); + $bench = $results->{bench}; + + if ($results->{sum} != $sum) { + logger("INVALID SUM $results->{sum} != $sum "); + $error = 1; + } + + $seconds += $bench->[0]; + $eventCount += $ENV{EVENT_SEND_COUNT}; + + if ($seconds >= $ENV{MIN_TEST_TIME}) { + last; + } + + logger("$iterations "); + } + + $_->{analysis} = { + cpuTime => $seconds, + eventCount => $eventCount, + eventsPerSecond => $eventCount / $seconds, + }; + + logger("done\n"); + } + + return ($error, @tests); +} + +sub doLog { + my (@tests) = @_; + + logger("Report format: file\tevents per second\tcost of solution\n"); + + foreach(sort({ $b->{analysis}->{eventsPerSecond} <=> $a->{analysis}->{eventsPerSecond} } @tests)) { + my $analysis = $_->{analysis}; + my $eventsPerSecond = int($analysis->{eventsPerSecond}); + our $fastest; + my $cost; + + if (! defined($fastest)) { + $fastest = $eventsPerSecond; + } + + $cost = $fastest / $eventsPerSecond; + + print "$_->{file}\t$eventsPerSecond\t$cost\n"; + } +} + diff --git a/bench/lib/EventBench/Reflex/Event.pm b/bench/lib/EventBench/Reflex/Event.pm new file mode 100755 index 0000000..85b26de --- /dev/null +++ b/bench/lib/EventBench/Reflex/Event.pm @@ -0,0 +1,13 @@ +package EventBench::Reflex::Event; + +use Moose; +use Reflex; + +extends 'Reflex::Event'; + +has arg1 => ( is => 'ro', isa => 'Num', required => 1 ); +has arg2 => ( is => 'ro', isa => 'Num', required => 1 ); + +__PACKAGE__->meta->make_immutable; + +1; diff --git a/bench/method-array.pl b/bench/method-array.pl new file mode 100755 index 0000000..51be31d --- /dev/null +++ b/bench/method-array.pl @@ -0,0 +1,27 @@ +package EventBench::Method::Array; + +use strict; +use warnings; +use Benchmark ':hireswallclock'; + +sub receive_event { + my ($arg1, $arg2) = @_; + our($sum); + + $sum += $arg1 + $arg2; +} + +return sub { + my (@testData) = @_; + our $sum = 0; + my $bench; + + $bench = timeit(1, sub { + + foreach(@testData) { + receive_event($_->[0], $_->[1]); + } + }); + + return { bench => $bench, sum => $sum }; +}; \ No newline at end of file diff --git a/bench/method-hash.pl b/bench/method-hash.pl new file mode 100755 index 0000000..70a1cad --- /dev/null +++ b/bench/method-hash.pl @@ -0,0 +1,26 @@ +package EventBench::Method::Hash; + +use strict; +use warnings; +use Benchmark ':hireswallclock'; + +sub receive_event { + my (%event) = @_; + our $sum; + + $sum += $event{arg1} + $event{arg2}; +} + +return sub { + my (@testData) = @_; + our $sum = 0; + my $bench; + + $bench = timeit(1, sub { + foreach(@testData) { + receive_event(arg1 => $_->[0], arg2 => $_->[1]); + } + }); + + return { bench => $bench, sum => $sum }; +}; \ No newline at end of file diff --git a/bench/objectmethod-array.pm b/bench/objectmethod-array.pm new file mode 100755 index 0000000..80e9ebe --- /dev/null +++ b/bench/objectmethod-array.pm @@ -0,0 +1,32 @@ +package EventBench::ObjectMethod::Array; + +use strict; +use warnings; +use Benchmark ':hireswallclock'; + +sub new { + return bless({}, $_[0]); +} + +sub receive_event { + my ($self, $arg1, $arg2) = @_; + our $sum; + + $sum += $arg1 + $arg2; +} + +return sub { + my (@testData) = @_; + my $test = EventBench::ObjectMethod::Array->new; + our $sum = 0; + my $bench; + + $bench = timeit(1, sub { + + foreach(@testData) { + $test->receive_event($_->[0], $_->[1]); + } + }); + + return { bench => $bench, sum => $sum }; +}; \ No newline at end of file diff --git a/bench/objectmethod-cbmanager.pm b/bench/objectmethod-cbmanager.pm new file mode 100755 index 0000000..1296146 --- /dev/null +++ b/bench/objectmethod-cbmanager.pm @@ -0,0 +1,57 @@ +package EventBench::ObjectMethod::CBManager; + +use strict; +use warnings; +use Benchmark ':hireswallclock'; +use Scalar::Util qw(weaken); + +sub new { + my $self = bless({ }, $_[0]); + our $sum; + + $self->{handlers}->{sum} = $self->weakcb(sub { + my ($self, %event) = @_; + + $sum += $event{arg1} + $event{arg2}; + }); + + return $self; +} + +sub receive_event { + my ($self, %event) = @_; + + $self->{handlers}->{$event{name}}->($self, %event); +} + +#pass in code ref or method name as string +sub weakcb { + my ($self, $cb) = @_; + + weaken($self); + + return sub { + my ($self) = shift(@_); + + die "expected weak reference to self to be valid but it was undefined" unless defined $self; + + return $self->$cb(@_); + }; +} + +return sub { + my (@testData) = @_; + my $test = EventBench::ObjectMethod::CBManager->new; + my $bench; + our $sum; + + $bench = timeit(1, sub { + our $sum = 0; + + foreach(@testData) { + $test->receive_event(name => 'sum', arg1 => $_->[0], arg2 => $_->[1]); + } + }); + + return { bench => $bench, sum => $sum }; +}; \ No newline at end of file diff --git a/bench/objectmethod-hash.pm b/bench/objectmethod-hash.pm new file mode 100755 index 0000000..e05ef43 --- /dev/null +++ b/bench/objectmethod-hash.pm @@ -0,0 +1,31 @@ +package EventBench::ObjectMethod::Hash; + +use strict; +use warnings; +use Benchmark ':hireswallclock'; + +sub new { + return bless({}, $_[0]); +} + +sub receive_event { + my ($self, %event) = @_; + our $sum; + + $sum += $event{arg1} + $event{arg2}; +} + +return sub { + my (@testData) = @_; + my $test = EventBench::ObjectMethod::Hash->new; + our $sum = 0; + my $bench; + + $bench = timeit(1, sub { + foreach(@testData) { + $test->receive_event(arg1 => $_->[0], arg2 => $_->[1]); + } + }); + + return { bench => $bench, sum => $sum }; +}; \ No newline at end of file