#!/usr/bin/perl # $Id: dont-guess-benchmark-it.pl 161 2009-07-22 21:03:25Z whynot $ # Copyright 2009 Eric Pozharski # 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 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, 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 (hereafter -- B) concentrates only on L. B by itself doesn't run any tests -- it's a plain generator of B code. The generated code must be in L and L domain -- that's implied explicitly in the generated code. Providing that BEB clean code is your responcibility. If you need funny tricks originated from B then you've picked wrong tool. Although, B checks aren't enabled. B operates like this: =over =item * After parsing the I<@ARGV> B 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 (IE\s+E>) is empty line anyway). =item * First could come a B section (if enabled, L>). That code will be put as-is between C and B invocation. =item * Then B 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. (B) No support for multiline snippets. (B) Editing capabilities are provided by I<$ENV{SHELL}>. (B) There should be a way to configure andZ<>Eor set snippet names at run-time. While snippet section is enabled by default (what would be worth benchmark of no code?) B itself doesn't check for it presence. If you omit snippet section, B will generate empty hash, and L has more. =item * Then B comes (if enabled, L>). 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) 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> was requested) the same code is passed to B interpreter. =item * (B) In the generated code there will be terminating C<__END__> line. L 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 Command-line parsing. Features in use are C and arg-type checks. Thus, if I've got F correctly, I<2.24> is required. I<2.37> works for me. =item B That's not required. But the generated code won't compile without it. That seems, that B 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 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 of B. It's mutually exclusive with L>. L and I<--count>> has more. =item I<--dump> =item I<-d> That flag (disabled by default) turns off pipeing B 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 has more. =item I<--preamble> =item I<-p> Enables preamble section. L 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 of B). It's mutually exclusive with L>. L 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 doesn't know what to choose. =item C The main reason would be that a process on the other side of pipe (mostly B) already died. (BTW, in that case there's no explanation after colon, for me.) (Me wonders, isn't B supposed to be killed with C in this case?) The main reason would be that just generated code fails F or F. (That means, that's your fault!) =item C Obvious. =item C B of L 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 doesn't provide such restriction internally and such extra check inside of B would void any use of internal of B 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, Ewhynot@cpan.orgZ<>E =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?) Should it be GNU APLv3?) =cut # vim: set filetype=perl