This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
bench.pl: add 'compile' benchmark field
[perl5.git] / t / perf / benchmarks
index 5526f8e..423230a 100644 (file)
 #     string::   string handling
 #
 #
-# Each hash has three fields:
+# Each hash has up to five fields:
+#
+#   desc  is a description of the test; if not present, it defaults
+#           to the same value as the 'code' field
+#
+#   setup is an optional string containing setup code that is run once
 #
-#   desc is a description of the test
-#   setup is a string containing setup code
 #   code  is a string containing the code to run in a loop
 #
-# So typically a benchmark tool might do something like
+#   pre   is an optional string containing setup code which is executed
+#         just before 'code' for every iteration, but whose execution
+#         time is not included in the result
+#
+#   post  like pre, but executed just after 'code'.
 #
-#   eval "package $token; $setup; for (1..1000000) { $code }"
+# So typically a benchmark tool might execute variations on something like
+#
+#   eval "package $name; $setup; for (1..1000000) { $pre; $code; $post }"
 #
 # Currently the only tool that uses this file is Porting/bench.pl;
 # try C<perl Porting/bench.pl --help> for more info
@@ -57,7 +66,9 @@
 # Note: for the cachegrind variant, an entry like
 #    'foo::bar' => {
 #     setup   => 'SETUP',
+#     pre     => 'PRE',
 #     code    => 'CODE',
+#     post    => 'POST',
 #   }
 # creates two temporary perl sources looking like:
 #
 #        BEGIN { srand(0) }
 #        SETUP;
 #        for my $__loop__ (1..$ARGV[0]) {
-#            1;
+#            PRE; 1; POST;
 #        }
 #
-# and as above, but with the '1;' in the loop  body replaced with:
+# and as above, but with the loop body replaced with:
 #
-#            CODE;
+#            PRE; CODE; POST;
 #
 # It then pipes each of the two sources into
 #
 # where N is set to 10 and then 20.
 #
 # It then uses the result of those four cachegrind runs to subtract out
-# the perl startup and loop overheads. So only what's in SETUP and CODE
-# can affect the benchmark, and if the loop happens to leave some state
-# changed (such as storing a value in a hash), then the final benchmark
-# timing is the result of running CODE with the hash entry populated
-# rather than empty.
+# the perl startup and loop overheads (including SETUP, PRE and POST), leaving
+# (in theory only CODE);
+#
+# Note that misleading results may be obtained if each iteration is
+# not identical. For example with
+#
+#     code => '$x .= "foo"',
+#
+# the string $x gets longer on each iteration. Similarly, a hash might be
+# empty on the first iteration, but have entries on subsequent iterations.
+#
+# To avoid this, use 'pre' or 'post', e.g.
+#
+#     pre  => '$x  = ""',
+#     code => '$x .= "foo"',
+#
+# Finally, the optional 'compile' key causes the code body to be wrapped
+# in eval qw{ sub { ... }}, so that compile time rather than execution
+# time is measured.
 
 
 [
         code    => '($r||0)->[0][0][0]',
     },
 
+    'expr::array::lex_bool_empty' => {
+        desc    => 'empty lexical array in boolean context',
+        setup   => 'my @a;',
+        code    => '!@a',
+    },
+    'expr::array::lex_bool_full' => {
+        desc    => 'non-empty lexical array in boolean context',
+        setup   => 'my @a = 1..10;',
+        code    => '!@a',
+    },
+    'expr::array::lex_scalar_empty' => {
+        desc    => 'empty lexical array in scalar context',
+        setup   => 'my (@a, $i);',
+        code    => '$i = @a',
+    },
+    'expr::array::lex_scalar_full' => {
+        desc    => 'non-empty lexical array in scalar context',
+        setup   => 'my @a = 1..10; my $i',
+        code    => '$i = @a',
+    },
+    'expr::array::pkg_bool_empty' => {
+        desc    => 'empty lexical array in boolean context',
+        setup   => 'our @a;',
+        code    => '!@a',
+    },
+    'expr::array::pkg_bool_full' => {
+        desc    => 'non-empty lexical array in boolean context',
+        setup   => 'our @a = 1..10;',
+        code    => '!@a',
+    },
+    'expr::array::pkg_scalar_empty' => {
+        desc    => 'empty lexical array in scalar context',
+        setup   => 'our @a; my $i;',
+        code    => '$i = @a',
+    },
+    'expr::array::pkg_scalar_full' => {
+        desc    => 'non-empty lexical array in scalar context',
+        setup   => 'our @a = 1..10; my $i',
+        code    => '$i = @a',
+    },
 
     'expr::arrayhash::lex_3var' => {
         desc    => 'lexical $h{$k1}[$i]{$k2}',
         code    => '($r||0)->{foo}{bar}{baz}',
     },
 
-
     'expr::hash::pkg_1const' => {
         desc    => 'package $hash{const}',
         setup   => '%h = ("foo" => 1)',
         setup   => 'my ($k1,$k2) = qw(foo bar); my %h = ($k1 => { $k2 => 1 });',
         code    => 'exists $h{$k1}{$k2}',
     },
+
+    'expr::hash::bool_empty' => {
+        desc    => 'empty lexical hash in boolean context',
+        setup   => 'my %h;',
+        code    => '!%h',
+    },
+    'expr::hash::bool_empty_unknown' => {
+        desc    => 'empty lexical hash in unknown context',
+        setup   => 'my ($i, %h); sub f { if (%h) { $i++ }}',
+        code    => 'f()',
+    },
+    'expr::hash::bool_full' => {
+        desc    => 'non-empty lexical hash in boolean context',
+        setup   => 'my %h = 1..10;',
+        code    => '!%h',
+    },
+
+
+    (
+        map {
+            sprintf('expr::hash::notexists_lex_keylen%04d',$_) => {
+                desc    => 'exists on non-key of length '. $_,
+                setup   => 'my %h; my $key = "A" x ' . $_ . '; $h{$key."x"} = 1;',
+                code    => 'exists $h{$key}',
+            },
+        } (
+            1 .. 24,
+            # 1,2,3,7,8,9,14,15,16,20,24,
+            50,
+            100,
+            1000,
+        )
+    ),
+    (
+        map {
+            sprintf('expr::hash::exists_lex_keylen%04d',$_) => {
+                desc    => 'exists on existing key of length '. $_,
+                setup   => 'my %h; my $key = "A" x ' . $_ . '; $h{$key} = 1;',
+                code    => 'exists $h{$key}',
+            },
+        } (
+            1 .. 24,
+            # 1,2,3,7,8,9,14,15,16,20,24,
+            50,
+            100,
+            1000,
+        )
+    ),
+
     'expr::hash::delete_lex_2var' => {
         desc    => 'lexical delete $hash{$k1}{$k2}',
         setup   => 'my ($k1,$k2) = qw(foo bar); my %h = ($k1 => { $k2 => 1 });',
         code    => '($x,$y,$z) = @_',
     },
 
+    # (....) = %lexical
+
+    'expr::aassign::ma_lh' => {
+        desc    => 'my array assigned lexical hash',
+        setup   => 'my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => 'my @a = %h',
+    },
+
 
     # (....) = ($lex1,$lex2,$lex3);
 
         code    => '($r1, $r2) = ([], []);',
     },
 
+    'expr::aassign::boolean' => {
+        desc    => '!(@a = @b)',
+        setup   => 'my ($s,@a, @b); @b = (1,2)',
+        code    => '!(@a = @b);',
+    },
+    'expr::aassign::scalar' => {
+        desc    => '$scalar = (@a = @b)',
+        setup   => 'my ($s, @a, @b); @b = (1,2)',
+        code    => '$s = (@a = @b);',
+    },
+
     # array assign of strings
 
     'expr::aassign::la_3s' => {
         code    => '$z = $x * $y',
     },
 
+    # use '!' to test SvTRUE on various classes of value
+
+    'expr::arith::not_PL_undef' => {
+        desc    => '!undef (using PL_sv_undef)',
+        setup   => 'my $x',
+        code    => '$x = !undef',
+    },
+    'expr::arith::not_PL_no' => {
+        desc    => '!($x == $y) (using PL_sv_no)',
+        setup   => 'my ($x, $y) = (1,2); my $z;',
+        code    => '$z = !($x == $y)',
+    },
+    'expr::arith::not_PL_zero' => {
+        desc    => '!%h (using PL_sv_zero)',
+        setup   => 'my ($x, %h)',
+        code    => '$x = !%h',
+    },
+    'expr::arith::not_PL_yes' => {
+        desc    => '!($x == $y) (using PL_sv_yes)',
+        setup   => 'my ($x, $y) = (1,1); my $z;',
+        code    => '$z = !($x == $y)',
+    },
+    'expr::arith::not_undef' => {
+        desc    => '!$y where $y is undef',
+        setup   => 'my ($x, $y)',
+        code    => '$x = !$y',
+    },
+    'expr::arith::not_0' => {
+        desc    => '!$x where $x is 0',
+        setup   => 'my ($x, $y) = (0, 0)',
+        code    => '$y = !$x',
+    },
+    'expr::arith::not_1' => {
+        desc    => '!$x where $x is 1',
+        setup   => 'my ($x, $y) = (1, 0)',
+        code    => '$y = !$x',
+    },
+    'expr::arith::not_string' => {
+        desc    => '!$x where $x is "foo"',
+        setup   => 'my ($x, $y) = ("foo", 0)',
+        code    => '$y = !$x',
+    },
+    'expr::arith::not_ref' => {
+        desc    => '!$x where $s is an array ref',
+        setup   => 'my ($x, $y) = ([], 0)',
+        code    => '$y = !$x',
+    },
+
     'expr::arith::preinc' => {
-        desc    => '++$x',
         setup   => 'my $x = 1;',
         code    => '++$x',
     },
     'expr::arith::predec' => {
-        desc    => '--$x',
         setup   => 'my $x = 1;',
         code    => '--$x',
     },
     },
 
 
+    'func::grep::bool0' => {
+        desc    => 'grep returning 0 items in boolean context',
+        setup   => 'my @a;',
+        code    => '!grep $_, @a;',
+    },
+    'func::grep::bool1' => {
+        desc    => 'grep returning 1 item in boolean context',
+        setup   => 'my @a =(1);',
+        code    => '!grep $_, @a;',
+    },
+    'func::grep::scalar0' => {
+        desc    => 'returning 0 items in scalar context',
+        setup   => 'my $g; my @a;',
+        code    => '$g = grep $_, @a;',
+    },
+    'func::grep::scalar1' => {
+        desc    => 'returning 1 item in scalar context',
+        setup   => 'my $g; my @a =(1);',
+        code    => '$g = grep $_, @a;',
+    },
+
+    # (index() == -1) and variants optimise away the op_const and op_eq
+    # and any assignment to a lexical var
+    'func::index::bool' => {
+        desc    => '(index() == -1) for match',
+        setup   => 'my $x = "aaaab"',
+        code    => 'index($x, "b") == -1',
+    },
+    'func::index::bool_fail' => {
+        desc    => '(index() == -1) for no match',
+        setup   => 'my $x = "aaaab"',
+        code    => 'index($x, "c") == -1',
+    },
+    'func::index::lex_bool' => {
+        desc    => '$lex = (index() == -1) for match',
+        setup   => 'my $r; my $x = "aaaab"',
+        code    => '$r = index($x, "b") == -1',
+    },
+    'func::index::lex_bool_fail' => {
+        desc    => '$lex = (index() == -1) for no match',
+        setup   => 'my $r; my $x = "aaaab"',
+        code    => '$r = index($x, "c") == -1',
+    },
 
     # using a const string as second arg to index triggers using FBM.
     # the FBM matcher special-cases 1,2-byte strings.
     },
 
 
+    'func::keys::lex::void_cxt_empty' => {
+        desc    => ' keys() on an empty lexical hash in void context',
+        setup   => 'my %h = ()',
+        code    => 'keys %h',
+    },
+    'func::keys::lex::void_cxt' => {
+        desc    => ' keys() on a non-empty lexical hash in void context',
+        setup   => 'my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => 'keys %h',
+    },
+    'func::keys::lex::bool_cxt_empty' => {
+        desc    => ' keys() on an empty lexical hash in bool context',
+        setup   => 'my %h = ()',
+        code    => '!keys %h',
+    },
+    'func::keys::lex::bool_cxt' => {
+        desc    => ' keys() on a non-empty lexical hash in bool context',
+        setup   => 'my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '!keys %h',
+    },
+    'func::keys::lex::scalar_cxt_empty' => {
+        desc    => ' keys() on an empty lexical hash in scalar context',
+        setup   => 'my $k; my %h = ()',
+        code    => '$k = keys %h',
+    },
+    'func::keys::lex::scalar_cxt' => {
+        desc    => ' keys() on a non-empty lexical hash in scalar context',
+        setup   => 'my $k; my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '$k = keys %h',
+    },
+    'func::keys::lex::list_cxt_empty' => {
+        desc    => ' keys() on an empty lexical hash in list context',
+        setup   => 'my %h = ()',
+        code    => '() = keys %h',
+    },
+    'func::keys::lex::list_cxt' => {
+        desc    => ' keys() on a non-empty lexical hash in list context',
+        setup   => 'my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '() = keys %h',
+    },
+
+    'func::keys::pkg::void_cxt_empty' => {
+        desc    => ' keys() on an empty package hash in void context',
+        setup   => 'our %h = ()',
+        code    => 'keys %h',
+    },
+    'func::keys::pkg::void_cxt' => {
+        desc    => ' keys() on a non-empty package hash in void context',
+        setup   => 'our %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => 'keys %h',
+    },
+    'func::keys::pkg::bool_cxt_empty' => {
+        desc    => ' keys() on an empty package hash in bool context',
+        setup   => 'our %h = ()',
+        code    => '!keys %h',
+    },
+    'func::keys::pkg::bool_cxt' => {
+        desc    => ' keys() on a non-empty package hash in bool context',
+        setup   => 'our %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '!keys %h',
+    },
+    'func::keys::pkg::scalar_cxt_empty' => {
+        desc    => ' keys() on an empty package hash in scalar context',
+        setup   => 'my $k; our %h = ()',
+        code    => '$k = keys %h',
+    },
+    'func::keys::pkg::scalar_cxt' => {
+        desc    => ' keys() on a non-empty package hash in scalar context',
+        setup   => 'my $k; our %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '$k = keys %h',
+    },
+    'func::keys::pkg::list_cxt_empty' => {
+        desc    => ' keys() on an empty package hash in list context',
+        setup   => 'our %h = ()',
+        code    => '() = keys %h',
+    },
+    'func::keys::pkg::list_cxt' => {
+        desc    => ' keys() on a non-empty package hash in list context',
+        setup   => 'our %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '() = keys %h',
+    },
+
+
+    'func::length::bool0' => {
+        desc    => 'length==0 in boolean context',
+        setup   => 'my $s = "";',
+        code    => '!length($s);',
+    },
+    'func::length::bool10' => {
+        desc    => 'length==10 in boolean context',
+        setup   => 'my $s = "abcdefghijk";',
+        code    => '!length($s);',
+    },
+    'func::length::scalar10' => {
+        desc    => 'length==10 in scalar context',
+        setup   => 'my $p; my $s = "abcdefghijk";',
+        code    => '$p = length($s);',
+    },
+    'func::length::bool0_utf8' => {
+        desc    => 'utf8 string length==0 in boolean context',
+        setup   => 'my $s = "\x{100}"; chop $s;',
+        code    => '!length($s);',
+    },
+    'func::length::bool10_utf8' => {
+        desc    => 'utf8 string length==10 in boolean context',
+        setup   => 'my $s = "abcdefghij\x{100}";',
+        code    => '!length($s);',
+    },
+    'func::length::scalar10_utf8' => {
+        desc    => 'utf8 string length==10 in scalar context',
+        setup   => 'my $p; my $s = "abcdefghij\x{100}";',
+        code    => '$p = length($s);',
+    },
+
+    'func::pos::bool0' => {
+        desc    => 'pos==0 in boolean context',
+        setup   => 'my $s = "abc"; pos($s) = 0',
+        code    => '!pos($s);',
+    },
+    'func::pos::bool10' => {
+        desc    => 'pos==10 in boolean context',
+        setup   => 'my $s = "abcdefghijk"; pos($s) = 10',
+        code    => '!pos($s);',
+    },
+    'func::pos::scalar10' => {
+        desc    => 'pos==10 in scalar context',
+        setup   => 'my $p; my $s = "abcdefghijk"; pos($s) = 10',
+        code    => '$p = pos($s);',
+    },
+
+    'func::ref::notaref_bool' => {
+        desc    => 'ref($notaref) in boolean context',
+        setup   => 'my $r = "boo"',
+        code    => '!ref $r',
+    },
+    'func::ref::ref_bool' => {
+        desc    => 'ref($ref) in boolean context',
+        setup   => 'my $r = []',
+        code    => '!ref $r',
+    },
+    'func::ref::blessedref_bool' => {
+        desc    => 'ref($blessed_ref) in boolean context',
+        setup   => 'my $r = bless []',
+        code    => '!ref $r',
+    },
+
+    'func::ref::notaref' => {
+        desc    => 'ref($notaref) in scalar context',
+        setup   => 'my $x; my $r = "boo"',
+        code    => '$x = ref $r',
+    },
+    'func::ref::ref' => {
+        desc    => 'ref($ref) in scalar context',
+        setup   => 'my $x; my $r = []',
+        code    => '$x = ref $r',
+    },
+    'func::ref::blessedref' => {
+        desc    => 'ref($blessed_ref) in scalar context',
+        setup   => 'my $x; my $r = bless []',
+        code    => '$x = ref $r',
+    },
+
+
 
     'func::sort::num' => {
         desc    => 'plain numeric sort',
     },
 
 
+    'func::sprintf::d' => {
+        desc    => '%d',
+        setup   => 'my $s; my $a1 = 1234;',
+        code    => '$s = sprintf "%d", $a1',
+    },
+    'func::sprintf::d8' => {
+        desc    => '%8d',
+        setup   => 'my $s; my $a1 = 1234;',
+        code    => '$s = sprintf "%8d", $a1',
+    },
+    'func::sprintf::foo_d8' => {
+        desc    => 'foo=%8d',
+        setup   => 'my $s; my $a1 = 1234;',
+        code    => '$s = sprintf "foo=%8d", $a1',
+    },
+
+    'func::sprintf::f0' => {
+        # "%.0f" is very special-cased
+        desc    => 'sprintf "%.0f"',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "%.0f", $a1',
+    },
+    'func::sprintf::foo_f0' => {
+        # "...%.0f..." is special-cased
+        desc    => 'sprintf "foo=%.0f"',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "foo=%.0f\n", $a1',
+    },
+    'func::sprintf::foo_f93' => {
+        desc    => 'foo=%9.3f',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "foo=%9.3f\n", $a1',
+    },
+
+    'func::sprintf::g9' => {
+        # "...%.NNNg..." is special-cased
+        desc    => '%.9g',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "%.9g", $a1',
+    },
+    'func::sprintf::foo_g9' => {
+        # "...%.NNNg..." is special-cased
+        desc    => 'foo=%.9g',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "foo=%.9g\n", $a1',
+    },
+    'func::sprintf::foo_g93' => {
+        desc    => 'foo=%9.3g',
+        setup   => 'my $s; my $a1 = 123.456;',
+        code    => '$s = sprintf "foo=%9.3g\n", $a1',
+    },
+
+    'func::sprintf::s' => {
+        desc    => '%s',
+        setup   => 'my $s; my $a1 = "abcd";',
+        code    => '$s = sprintf "%s", $a1',
+    },
+    'func::sprintf::foo_s' => {
+        desc    => 'foo=%s',
+        setup   => 'my $s; my $a1 = "abcd";',
+        code    => '$s = sprintf "foo=%s", $a1',
+    },
+    'func::sprintf::mixed_utf8_s' => {
+        desc    => 'foo=%s bar=%s baz=%s',
+        setup   => 'my $s;',
+        code    => '$s = sprintf "foo=%s", "ab\x{100}cd", "efg", "h\x{101}ij"',
+    },
+
+    'func::subst::bool' => {
+        desc    => 's/// in boolean context',
+        setup   => '',
+        code    => '$_ = "aaa"; !s/./x/g;'
+    },
+
+
+    'func::values::scalar_cxt_empty' => {
+        desc    => ' values() on an empty hash in scalar context',
+        setup   => 'my $k; my %h = ()',
+        code    => '$k = values %h',
+    },
+    'func::values::scalar_cxt' => {
+        desc    => ' values() on a non-empty hash in scalar context',
+        setup   => 'my $k; my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '$k = values %h',
+    },
+    'func::values::list_cxt_empty' => {
+        desc    => ' values() on an empty hash in list context',
+        setup   => 'my %h = ()',
+        code    => '() = values %h',
+    },
+    'func::values::list_cxt' => {
+        desc    => ' values() on a non-empty hash in list context',
+        setup   => 'my %h = qw(aardvark 1 banana 2 cucumber 3)',
+        code    => '() = values %h',
+    },
+
+
+
     'loop::block' => {
         desc    => 'empty basic loop',
         setup   => '',
         setup   => 'my $i = 0;',
         code    => 'while (++$i % 4) {}',
     },
-    (
-        map {
-            sprintf('hash::set1k::len_%04d',$_) => {
-                desc    => 'hash keys length '. $_,
-                setup   => 'my $i = "A" x ' . $_ . '; my @s= map { $i++ } 1..1000;',
-                code    => 'my %h; @h{@s}=();',
-            },
-        } (
-            1..24,
-            50,
-            100,
-            1000,
-        )
-    ),
+
+
+    'regex::anyof_plus::anchored' => {
+        setup   => '$_ = "a" x 100;',
+        code    => '/^[acgt]+/',
+    },
+    'regex::anyof_plus::floating' => {
+        desc    => '/[acgt]+where match starts at position 0 for 100 chars/',
+        setup   => '$_ = "a" x 100;',
+        code    => '/[acgt]+/',
+    },
+    'regex::anyof_plus::floating_away' => {
+        desc    => '/[acgt]+/ where match starts at position 100 for 100 chars',
+        setup   => '$_ = ("0" x 100) . ("a" x 100);',
+        code    => '/[acgt]+/',
+    },
+
+    'regex::whilem::min_captures_fail' => {
+        desc    => '/WHILEM with anon-greedy match and captures that fails',
+        setup   => '$_ = ("a" x 20)',
+        code    => '/^(?:(.)(.))*?[XY]/',
+    },
+    'regex::whilem::max_captures_fail' => {
+        desc    => '/WHILEM with a greedy match and captures that fails',
+        setup   => '$_ = ("a" x 20)',
+        code    => '/^(?:(.)(.))*[XY]/',
+    },
 ];