use strictures 1; use Benchmark qw/:hireswallclock cmpthese/; use Getopt::Long::Descriptive; use Config; $ENV{PERL5LIB} = join ($Config{path_sep}, @INC); my ($opts, $usage); BEGIN { ($opts, $usage) = describe_options( '%c: %o' => [ 'help|h' => 'Print usage message and exit' ], [ 'classes|c:i' => 'How many classes to create per benchmark cycle (def 10)', { default => 10 } ], [ 'accessors|a:i' => 'How many accessors/attributes of each type to create per class (def 10)', { default => 10 } ], [ 'subprocess|startup|s' => 'Run the code in a subprocess to benchmark actual time spent on compilation' ], [ 'pregenerate|p:i' => 'How many bench-runs to pre-generate for compilation in case --subprocess is not used (def 1000)', { default => 1000} ], [ 'run|r' => 'Use each accessor at runtime (get/set/get cycle)' ], [ 'unique|u' => 'Make accessor names globally unique (instead of just per class)' ], [ 'bench|b:s' => 'Which benchmarks to run (all|xs|pp)', { default => 'all', regex => qr/^(?:all|xs|pp)$/ } ], { getopt_conf => [qw/gnu_getopt bundling_override no_ignore_case/] }, ); # can not change this runtime, thus in-block $ENV{MOUSE_PUREPERL} = 1 if $opts->{bench} eq 'pp'; my @missing; for (qw/ Moose Moo Mouse /) { eval "require $_" or push @missing, $_; } if (@missing) { die sprintf "Missing modules necessary for benchmark:\n\n%s\n\n", join ("\n", @missing); } } use Method::Generate::Accessor; # need to pre-load for the XS shut-off to work $usage->die if $opts->{help}; $opts->{pregenerate} = 1 if $opts->{subprocess}; my $counters; my $tasks = {}; my $attrs_to_bench = { plain => q|is => 'rw'|, lazy_default => q|is => 'rw', lazy => 1, default => sub { {} }|, lazy_default_qsub => q|is => 'rw', lazy => 1, default => Sub::Quote::quote_sub q{ {} }|, }; for (keys %$attrs_to_bench) { if ($opts->{bench} =~ /all|pp/) { { local $Method::Generate::Accessor::CAN_HAZ_XS = 0; _add_moosey_has (moo => 'Moo', $_); } _add_moosey_has (moose => 'Moose', $_); _add_moosey_has (mouse => 'Mouse', $_) if $ENV{MOUSE_PUREPERL}; } if ($opts->{bench} =~ /all|xs/) { { local $Method::Generate::Accessor::CAN_HAZ_XS = 1; _add_moosey_has (moo_XS => 'Moo', $_); } _add_moosey_has (mouse_XS => 'Mouse', $_) unless $ENV{MOUSE_PUREPERL}; } } # run each task once, prime whatever caches there may be $_->() for values %$tasks; # Actual Benchmarking for (1, 2) { print "Perl $], take $_:\n"; # if forking must run for certain number of cycles, cputime doesn't work foreach my $type (sort keys %$attrs_to_bench) { print "Benchming ${type}:\n"; my %these = map { (split ' ', $_)[0] => $tasks->{$_} } grep /${type}$/, keys %$tasks; cmpthese ( $opts->{subprocess} ? 15 : -1 , \%these ); } print "\n"; } exit 0; # the end sub _add_moosey_has { my ($name, $base, $attr_type) = @_; my @to_eval; for (1 .. $opts->{pregenerate} ) { my $perl = 'use Sub::Quote;'; for ( 1.. $opts->{classes} ) { my $class = "Bench::${base}_" . ++$counters->{class}; $perl .= "package $class; use $base;"; my @attr_names; for ( 1.. $opts->{accessors} ) { my $attr = "attribute_${attr_type}" . ++$counters->{acc}; push @attr_names, $attr; $perl .= "has $attr => ($attrs_to_bench->{$attr_type});"; } $perl .= '__PACKAGE__->meta->make_immutable;' if $name !~ /^moo(_XS)?$/; $counters->{accessors} = 0 unless $opts->{unique}; if ($opts->{run}) { $perl .= "\$::obj = $class->new;"; $perl .= "\$::foo = \$::obj->$_; \$::obj->$_(1); \$::foo = \$::obj->$_;" for @attr_names; } } push @to_eval, $perl; } $tasks->{"$name $attr_type"} = $opts->{subprocess} ? sub { open (my $subproc, '|-', $^X, '-'); print $subproc $to_eval[0]; close $subproc; } : sub { eval shift @to_eval; } ; }