In perl.git, the branch blead has been updated <http://perl5.git.perl.org/perl.git/commitdiff/58da6fbcb8d595318b667d7d7cc8739f5feb15f3?hp=5a1562d6aa780bfc9a14482654d4f7d63a1db386>
- Log ----------------------------------------------------------------- commit 58da6fbcb8d595318b667d7d7cc8739f5feb15f3 Author: Max Maischein <[email protected]> Date: Mon Jun 1 14:18:42 2009 +0200 Add benchmark test for keys() on empty hashes (RT26188) M MANIFEST A t/benchmark/rt26188-speed-up-keys-on-empty-hash.t commit 900ac0519e4b408fd93443b14b734cc330c59698 Author: Max Maischein <[email protected]> Date: Sun May 31 23:50:12 2009 +0200 Fix RT26188, speed up keys() on empty hash M hv.c ----------------------------------------------------------------------- Summary of changes: MANIFEST | 1 + hv.c | 40 +++++---- t/benchmark/rt26188-speed-up-keys-on-empty-hash.t | 91 +++++++++++++++++++++ 3 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 t/benchmark/rt26188-speed-up-keys-on-empty-hash.t diff --git a/MANIFEST b/MANIFEST index 0903a46..c6d5dcb 100644 --- a/MANIFEST +++ b/MANIFEST @@ -3858,6 +3858,7 @@ t/base/num.t See if numbers work t/base/pat.t See if pattern matching works t/base/rs.t See if record-read works t/base/term.t See if various terms work +t/benchmark/rt26188-speed-up-keys-on-empty-hash.t Benchmark if keys on empty hashes is fast enough t/cmd/elsif.t See if else-if works t/cmd/for.t See if for loops work t/cmd/mod.t See if statement modifiers work diff --git a/hv.c b/hv.c index 3322848..5f233d6 100644 --- a/hv.c +++ b/hv.c @@ -2143,26 +2143,32 @@ Perl_hv_iternext_flags(pTHX_ HV *hv, I32 flags) } } } - while (!entry) { - /* OK. Come to the end of the current list. Grab the next one. */ - iter->xhv_riter++; /* HvRITER(hv)++ */ - if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) { - /* There is no next one. End of the hash. */ - iter->xhv_riter = -1; /* HvRITER(hv) = -1 */ - break; - } - entry = (HvARRAY(hv))[iter->xhv_riter]; + /* Quick bailout if the hash is empty anyway. + I don't know if placeholders are included in the KEYS count, so a defensive check + */ + if (HvKEYS(hv) || (flags & HV_ITERNEXT_WANTPLACEHOLDERS)) { + while (!entry) { + /* OK. Come to the end of the current list. Grab the next one. */ + + iter->xhv_riter++; /* HvRITER(hv)++ */ + if (iter->xhv_riter > (I32)xhv->xhv_max /* HvRITER(hv) > HvMAX(hv) */) { + /* There is no next one. End of the hash. */ + iter->xhv_riter = -1; /* HvRITER(hv) = -1 */ + break; + } + entry = (HvARRAY(hv))[iter->xhv_riter]; - if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) { - /* If we have an entry, but it's a placeholder, don't count it. - Try the next. */ - while (entry && HeVAL(entry) == &PL_sv_placeholder) - entry = HeNEXT(entry); + if (!(flags & HV_ITERNEXT_WANTPLACEHOLDERS)) { + /* If we have an entry, but it's a placeholder, don't count it. + Try the next. */ + while (entry && HeVAL(entry) == &PL_sv_placeholder) + entry = HeNEXT(entry); + } + /* Will loop again if this linked list starts NULL + (for HV_ITERNEXT_WANTPLACEHOLDERS) + or if we run through it and find only placeholders. */ } - /* Will loop again if this linked list starts NULL - (for HV_ITERNEXT_WANTPLACEHOLDERS) - or if we run through it and find only placeholders. */ } if (oldentry && HvLAZYDEL(hv)) { /* was deleted earlier? */ diff --git a/t/benchmark/rt26188-speed-up-keys-on-empty-hash.t b/t/benchmark/rt26188-speed-up-keys-on-empty-hash.t new file mode 100644 index 0000000..43401ad --- /dev/null +++ b/t/benchmark/rt26188-speed-up-keys-on-empty-hash.t @@ -0,0 +1,91 @@ +#!/usr/bin/perl -w +use strict; +use Benchmark; +require './test.pl'; +skip_all("Benchmark tests not enabled") unless $ENV{PERL_BENCHMARK}; +plan(tests => 6); + +=head1 NAME + +rt26188 - benchmark speed for keys() on empty hashes + +=head1 DESCRIPTION + +If you have an empty hash, the speed of keys() depends +on how many keys the hash previously held. + +For global hashes, getting the count for previously +big hashes was substantially slower than for lexical hashes. + +This test checks that the speed difference for getting +the number or list of keys from an empty hash is about the same +(< 25%) for lexical and global hashes, both previously big and small. + +=head1 REFERENCE + +This test tests against RT ticket #26188 + +L<http://rt.perl.org/rt3/Public/Bug/Display.html?id=26188> + +=cut + +use vars qw(%h_big %h_small); +my %l_big = (1..50000); +my %l_small = (1..10); + +%h_big = (1..50000); +%h_small = (1..10); + +delete @h_big{keys %h_big}; +delete @h_small{keys %h_small}; +delete @l_big{keys %l_big}; +delete @l_small{keys %l_small}; + +my $res = timethese shift || -3, { + big => '1 for keys %h_big', + small => '1 for keys %h_small', + scalar_big => '$a = keys %h_big', + scalar_small => '$a = keys %h_small', + + lex_big => '1 for keys %l_big', + lex_small => '1 for keys %l_small', + lex_scalar_big => '$a = keys %l_big', + lex_scalar_small => '$a = keys %l_small', +}, 'none'; + +sub iters_per_second { + $_[0]->iters / $_[0]->cpu_p +} + +sub about_as_fast_ok { + my ($res, $key1, $key2, $name) = @_; + $name ||= "Speed difference between $key1 and $key2 is less than 25%"; + my %iters_per_second = map { $_ => iters_per_second( $res->{ $_ }) } ($key1, $key2); + + my $ratio = abs(1 - $iters_per_second{ $key1 } / ($iters_per_second{ $key2 } || 1 )); + if (! cmp_ok( $ratio, '<', 0.25, $name )) { + diag( sprintf "%20s: %12.2f/s\n", $key1, $iters_per_second{ $key1 } ); + diag( sprintf "%20s: %12.2f/s\n", $key2, $iters_per_second{ $key2 } ); + }; +}; + +about_as_fast_ok( $res, 'scalar_big', 'scalar_small',"Checking the count of hash keys in an empty hash (global)"); + +about_as_fast_ok( $res, 'big', 'small', "Checking the list of hash keys in an empty hash (global)"); + +about_as_fast_ok( $res, 'lex_scalar_big', 'lex_scalar_small',"Checking the count of hash keys in an empty hash (lexical)"); + +about_as_fast_ok( $res, 'lex_big', 'lex_small', "Checking the list of hash keys in an empty hash (lexical)"); + +about_as_fast_ok( $res, 'lex_scalar_big', 'scalar_big',"Checking the count of hash keys in an empty hash, global vs. lexical"); + +about_as_fast_ok( $res, 'lex_big', 'big', "Checking the list of hash keys in an empty hash, global vs. lexical"); + +__END__ + +# code written + /* quick bailout if the hash is empty anyway. + I don't know if placeholders are included in the KEYS count, so a defensive check + */ + if (! HvKEYS(hv) && !(flags & HV_ITERNEXT_WANTPLACEHOLDERS) ) + return NULL; -- Perl5 Master Repository
