#!/usr/bin/perl # $Id: dont-guess-benchmark-it.pl 161 2009-07-22 21:03:25Z whynot $ # Copyright 2009 Eric Pozharski <whynot@pozharski.name> # AS-IS/NO-WARRANTY/HOPE-TO-BE-USEFUL -- GNU GPLv3 package main; =head1 NAME dont-guess-benchmark-it - generator of perl code snippets benchmarks =head1 README Generate your I<-MBenchmark> code quickly. That's generator -- it doesn't benchmark itself. =head1 USAGE # that will request running each snippet for 250sec dont-guess-benchmark-it.pl --time 250 # that will request running each snippet twice dont-guess-benchmark-it.pl --count 2 # I<--time> and I<--count> are mutually exclusive # that flag places some code before B<cmpthese()> invocation dont-guess-benchmark-it.pl --preamble # that flag places some data in I<__DATA__> section, # but before I<__END__> dont-guess-benchmark-it.pl --postamble # that disables invoking B<perl>, only dumps ready to run code dont-guess-benchmark-it.pl --dump =head1 DESCRIPTION As requested by certain authorites, Perl developer shouldn't guess ("citation needed" (TM)). While I believe that quote has much wider scope, B<dont-guess-benchmark-it.pl> (hereafter -- B<dgbi>) concentrates only on L<Benchmark.pm>. B<dgbi> by itself doesn't run any tests -- it's a plain generator of B<perl> code. The generated code must be in L<strict.pm> and L<warnings.pm> domain -- that's implied explicitly in the generated code. Providing that B<strict>E<sol>B<warnings> clean code is your responcibility. If you need funny tricks originated from B<Perl4> then you've picked wrong tool. Although, B<taint> checks aren't enabled. B<dgbi> operates like this: =over =item * After parsing the I<@ARGV> B<dgbi> knows what sections are supposed to be in an input. =item * The sections are read from I<*STDIN> linewise. If there would be more than one section, then they are separated by empty lines (line of spaces (I<qrZ<>E<sol>\s+E<sol>>) is empty line anyway). =item * First could come a B<preamble> section (if enabled, L</I<--preamble>>). That code will be put as-is between C<use Benchmark;> and B<cmpthese()> invocation. =item * Then B<snippet> section comes. Each line will be wrapped in something like this: code00 => sub { 'here is your line, quotes omitted' }, and passed in anonymous HASH to B<timethese()>. (B<bug>) No support for multiline snippets. (B<bug>) Editing capabilities are provided by I<$ENV{SHELL}>. (B<bug>) There should be a way to configure andZ<>E<sol>or set snippet names at run-time. While snippet section is enabled by default (what would be worth benchmark of no code?) B<dgbi> itself doesn't check for it presence. If you omit snippet section, B<dgbi> will generate empty hash, and L</DIAGNOSTICS> has more. =item * Then B<postamble> comes (if enabled, L</I<--postamble>>). Those lines will be treated as data, but code (while linewise anyway) and will be put in I<__DATA__> section of the generated code. (If postamble wasn't requested, then I<__DATA__> section isn't generated.) (B<caveat>) If you request postamble while omitting any input, then empty I<__DATA__> section is generated. =item * Then just generated code is dumped on STDOUT, and (unless L</I<--dump>> was requested) the same code is passed to B<perl> interpreter. =item * (B<caveat>) In the generated code there will be terminating C<__END__> line. L<perlrun/DESCRIPTION> has more. =back =cut use strict; use warnings; #use version 0.50; use Getopt::Long qw| :config bundling |; my $VERSION = 0.000_007; =head1 PREREQUISITES Getopt::Long =head1 DEPENDENCIES =over =item B<Getopt::Long> Command-line parsing. Features in use are C<bundling> and arg-type checks. Thus, if I've got F<CHANGES> correctly, I<2.24> is required. I<2.37> works for me. =item B<Benchmark> That's not required. But the generated code won't compile without it. That seems, that B<Benchmark.pm> of September 1999 is OK (unknown version). I<1.1> works for me. =back =cut #=head1 OSNAMES #=head1 INCOMPATIBILITIES my %opts; GetOptions \%opts, qw[ help|h! version|v! dump|d! preamble|p! postamble|P! time|t=i count|c=i ] or die q|parsing command-line failed|; if( $opts{help} ) { print <<'END_OF_HELP'; input: optional preamble and separating empty line supposed snippets optional separating empty line and postamble REMEMBER: close input when you've finished options (don't use those 2 simultaneously): --time=INTEGER how long to loop (secs) -t INTEGER --count=INTEGER how many time to loop (count) -c INTEGER other options: --dump only dump code, don't run interpreter -d --preamble enable preamble part -p --postamble enable postamble part -P --help obvious -h --version obvious -v END_OF_HELP exit 0; } elsif( $opts{version} ) { printf <<'END_OF_VERSION', %s -- version: %f license: %s generated code license: %s END_OF_VERSION ( split m{/}, $0 )[-1], $VERSION, q|GNU GPLv3|, q|at user's option|; exit 0; }; $opts{time} && $opts{count} and die qq|--time and --count are mutually exclusive\n|; =head1 ARGUMENTS The only (somewhat) required argument is any input. While it's supposed to come from I<*STDIN>, B<dgbi> reads from I<*ARGV>, thus that's possible to call it this way dont-guess-benchmark-it.pl -p 'my $x;' '' '$x++' '++$x' or this (puzzled) dont-guess-benchmark-it.pl /dev/urandom =head1 OPTIONS =over =item I<--count> =item I<-c> Takes one required argument -- an integer decimal. Sets number of loops for B<timethese()> of B<Benchmark>. It's mutually exclusive with L</I<--time>>. L</I<--time> and I<--count>> has more. =item I<--dump> =item I<-d> That flag (disabled by default) turns off pipeing B<perl> interpreter. Thus only dumping the generated code happens. =item I<--help> =item I<-h> Obvious. =item I<--postamble> =item I<-P> Enables postamble section. L</DESCRIPTION> has more. =item I<--preamble> =item I<-p> Enables preamble section. L</DESCRIPTION> has more. =item I<--time> =item I<-t> Takes one required argument -- an integer decimal. Sets number of seconds to loop each snippet (passed to B<timethese()> of B<Benchmark>). It's mutually exclusive with L</I<--count>>. L</I<--time> and I<--count>> has more. =item I<--version> =item I<-v> Obvious. =back =cut my $empty_line = qr{^\s*$}; my $output = <<'END_OF_INPUT'; #!/usr/bin/perl use strict; use warnings; use Benchmark qw{ cmpthese timethese }; END_OF_INPUT if( $opts{preamble} ) { while( my $code = <> ) { $output .= $code; $code =~ m{$empty_line}o and last; }; }; $output .= sprintf qq|cmpthese timethese %i, {\n|, $opts{count} || ($opts{time} && -$opts{time}) || -5; my $code_base = ($. || 1) + ($opts{preamble} ? 1 : 0); while( my $code = <> ) { chomp $code; $code =~ m{$empty_line}o and last; $output .= sprintf qq| code%02i => sub { %s },\n|, $. - $code_base, $code; }; $output .= qq|};\n\n|; if( $opts{postamble} ) { $output .= qq|__DATA__\n|; while( my $code = <> ) { $output .= $code; $code =~ m{$empty_line}o and last; }; }; $output .= qq|__END__\n|; unless( $opts{dump} ) { print $output; open STDOUT, q{|-}, qw| /usr/bin/perl | or die qq|can't fork (perl): $!|; }; print $output; close STDOUT or die qq|can't close (STDOUT): $!|; =head1 DIAGNOSTICS =over =item C<--time and --count are mutually exclusive> You can't set them both. B<dgbi> doesn't know what to choose. =item C<can't close (STDOUT): %s> The main reason would be that a process on the other side of pipe (mostly B<perl>) already died. (BTW, in that case there's no explanation after colon, for me.) (Me wonders, isn't B<dgbi> supposed to be killed with C<SIGPIPE> in this case?) The main reason would be that just generated code fails F<strict.pm> or F<warnings.pm>. (That means, that's your fault!) =item C<can't fork (perl): %s> Obvious. =item C<parsing command-line failed> B<GetOptions()> of L<Getopt::Long> returned false. Any reasons are supposed to come before. =back =head1 NOTES =over =item bugs and caveats All (?) mentions are spread within this POD. =item I<--time> and I<--count> The values for these should be positive integers. Because B<Getopt::Long> doesn't provide such restriction internally and such extra check inside of B<dgbi> would void any use of internal of B<G::L> check, any integer is accepted. Thus, those pairs are each technically equivalent: dont-gusess-benchmark-it.pl --count 5 dont-guesss-benchmark-it.pl --time -5 and dont-guess-benchmark-it.pl --time 5 dont-guess-benchmark-it.pl --count -5 =back =head1 SCRIPT CATEGORIES Educational/ComputerScience =head1 AUTHOR Eric Pozharski, E<lt>whynot@cpan.orgZ<>E<gt> =head1 COPYRIGHT & LICENSE Copyright 2009 by Eric Pozharski This utility is free in sense: AS-IS, NO-WARRANTY, HOPE-TO-BE-USEFUL. This utility is released under GNU GPLv3. The License of the Generated Code is at option of the User. ((B<bug>?) Should it be GNU APLv3?) =cut # vim: set filetype=perl