<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl
# $Id: dont-guess-benchmark-it.pl 161 2009-07-22 21:03:25Z whynot $
# Copyright 2009 Eric Pozharski &lt;whynot@pozharski.name&gt;
# 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&lt;-MBenchmark&gt; 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&lt;--time&gt; and I&lt;--count&gt; are mutually exclusive

    # that flag places some code before B&lt;cmpthese()&gt; invocation
    dont-guess-benchmark-it.pl --preamble
    # that flag places some data in I&lt;__DATA__&gt; section,
    # but before I&lt;__END__&gt;
    dont-guess-benchmark-it.pl --postamble

    # that disables invoking B&lt;perl&gt;, 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&lt;dont-guess-benchmark-it.pl&gt;
(hereafter -- B&lt;dgbi&gt;) concentrates only on L&lt;Benchmark.pm&gt;.
B&lt;dgbi&gt; by itself doesn't run any tests -- it's a plain generator of B&lt;perl&gt;
code.
The generated code must be in L&lt;strict.pm&gt; and L&lt;warnings.pm&gt; domain --
that's implied explicitly in the generated code.
Providing that B&lt;strict&gt;E&lt;sol&gt;B&lt;warnings&gt; clean code is your responcibility.
If you need funny tricks originated from B&lt;Perl4&gt; then you've picked wrong
tool.
Although, B&lt;taint&gt; checks aren't enabled.

B&lt;dgbi&gt; operates like this:

=over

=item *

After parsing the I&lt;@ARGV&gt; B&lt;dgbi&gt; knows what sections are supposed to be in an
input.

=item *

The sections are read from I&lt;*STDIN&gt; linewise.
If there would be more than one section, then they are separated by empty lines
(line of spaces (I&lt;qrZ&lt;&gt;E&lt;sol&gt;\s+E&lt;sol&gt;&gt;) is empty line anyway).

=item *

First could come a B&lt;preamble&gt; section (if enabled, L&lt;/I&lt;--preamble&gt;&gt;).
That code will be put as-is between C&lt;use Benchmark;&gt; and B&lt;cmpthese()&gt;
invocation.

=item *

Then B&lt;snippet&gt; section comes.
Each line will be wrapped in something like this:

    code00 =&gt; sub { 'here is your line, quotes omitted' },

and passed in anonymous HASH to B&lt;timethese()&gt;.

(B&lt;bug&gt;)
No support for multiline snippets.

(B&lt;bug&gt;)
Editing capabilities are provided by I&lt;$ENV{SHELL}&gt;.

(B&lt;bug&gt;)
There should be a way to configure andZ&lt;&gt;E&lt;sol&gt;or set snippet names at
run-time.

While snippet section is enabled by default
(what would be worth benchmark of no code?)
B&lt;dgbi&gt; itself doesn't check for it presence.
If you omit snippet section, B&lt;dgbi&gt; will generate empty hash, and
L&lt;/DIAGNOSTICS&gt; has more.

=item *

Then B&lt;postamble&gt; comes (if enabled, L&lt;/I&lt;--postamble&gt;&gt;).
Those lines will be treated as data, but code (while linewise anyway) and will
be put in I&lt;__DATA__&gt; section of the generated code.
(If postamble wasn't requested, then I&lt;__DATA__&gt; section isn't generated.)

(B&lt;caveat&gt;)
If you request postamble while omitting any input, then empty I&lt;__DATA__&gt;
section is generated.

=item *

Then just generated code is dumped on STDOUT, and (unless L&lt;/I&lt;--dump&gt;&gt; was
requested) the same code is passed to B&lt;perl&gt; interpreter.

=item *

(B&lt;caveat&gt;)
In the generated code there will be terminating C&lt;__END__&gt; line.
L&lt;perlrun/DESCRIPTION&gt; 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&lt;Getopt::Long&gt;

Command-line parsing.
Features in use are C&lt;bundling&gt; and arg-type checks.
Thus, if I've got F&lt;CHANGES&gt; correctly, I&lt;2.24&gt; is required.
I&lt;2.37&gt; works for me.

=item B&lt;Benchmark&gt;

That's not required.
But the generated code won't compile without it.
That seems, that B&lt;Benchmark.pm&gt; of September 1999 is OK (unknown version).
I&lt;1.1&gt; 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 &lt;&lt;'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 &lt;&lt;'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} &amp;&amp; $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&lt;*STDIN&gt;, B&lt;dgbi&gt; reads from I&lt;*ARGV&gt;, 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&lt;--count&gt;

=item I&lt;-c&gt;

Takes one required argument -- an integer decimal.
Sets number of loops for B&lt;timethese()&gt; of B&lt;Benchmark&gt;.
It's mutually exclusive with L&lt;/I&lt;--time&gt;&gt;.
L&lt;/I&lt;--time&gt; and I&lt;--count&gt;&gt; has more.

=item I&lt;--dump&gt;

=item I&lt;-d&gt;

That flag (disabled by default) turns off pipeing B&lt;perl&gt; interpreter.
Thus only dumping the generated code happens.

=item I&lt;--help&gt;

=item I&lt;-h&gt;

Obvious.

=item I&lt;--postamble&gt;

=item I&lt;-P&gt;

Enables postamble section.
L&lt;/DESCRIPTION&gt; has more.

=item I&lt;--preamble&gt;

=item I&lt;-p&gt;

Enables preamble section.
L&lt;/DESCRIPTION&gt; has more.

=item I&lt;--time&gt;

=item I&lt;-t&gt;

Takes one required argument -- an integer decimal.
Sets number of seconds to loop each snippet
(passed to B&lt;timethese()&gt; of B&lt;Benchmark&gt;).
It's mutually exclusive with L&lt;/I&lt;--count&gt;&gt;.
L&lt;/I&lt;--time&gt; and I&lt;--count&gt;&gt; has more.

=item I&lt;--version&gt;

=item I&lt;-v&gt;

Obvious.

=back

=cut

my $empty_line = qr{^\s*$};

my $output = &lt;&lt;'END_OF_INPUT';
#!/usr/bin/perl

use strict;
use warnings;
use Benchmark qw{ cmpthese timethese };

END_OF_INPUT

if( $opts{preamble} )         {
    while( my $code = &lt;&gt; ) {
        $output .= $code;
        $code =~ m{$empty_line}o and
          last;             }; };

$output .=
  sprintf qq|cmpthese timethese %i, {\n|,
    $opts{count} || ($opts{time} &amp;&amp; -$opts{time}) || -5;

my $code_base = ($. || 1) + ($opts{preamble} ? 1 : 0);
while( my $code = &lt;&gt; )       {
    chomp $code;
    $code =~ m{$empty_line}o and
      last;
    $output .= sprintf qq|  code%02i =&gt; sub { %s },\n|,
      $. - $code_base, $code; };

$output .= qq|};\n\n|;

if( $opts{postamble} )        {
    $output .= qq|__DATA__\n|;
    while( my $code = &lt;&gt; ) {
        $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&lt;--time and --count are mutually exclusive&gt;

You can't set them both.
B&lt;dgbi&gt; doesn't know what to choose.

=item C&lt;can't close (STDOUT): %s&gt;

The main reason would be that a process on the other side of pipe
(mostly B&lt;perl&gt;)
already died.
(BTW, in that case there's no explanation after colon, for me.)
(Me wonders, isn't B&lt;dgbi&gt; supposed to be killed with C&lt;SIGPIPE&gt; in this case?)
The main reason would be that just generated code fails F&lt;strict.pm&gt; or
F&lt;warnings.pm&gt;.
(That means, that's your fault!)

=item C&lt;can't fork (perl): %s&gt;

Obvious.

=item C&lt;parsing command-line failed&gt;

B&lt;GetOptions()&gt; of L&lt;Getopt::Long&gt; 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&lt;--time&gt; and I&lt;--count&gt;

The values for these should be positive integers.
Because B&lt;Getopt::Long&gt; doesn't provide such restriction internally and such
extra check inside of B&lt;dgbi&gt; would void any use of internal of B&lt;G::L&gt; 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;lt&gt;whynot@cpan.orgZ&lt;&gt;E&lt;gt&gt;

=head1 COPYRIGHT &amp; 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&lt;bug&gt;?) Should it be GNU APLv3?)

=cut

# vim: set filetype=perl
</pre></body></html>