[Tokyo.pm] Re: わかりやすいソート (Re: [Tokyo.pm] Re: [Tokyo.pm] JUS感想)

scozens @ pwj.co.jp scozens @ pwj.co.jp
1999年 10月 6日 (水) 22:56:52 CDT





前田さん:
    wrong:
     @keys1 = map { getkey1($_) } @unsorted;
     @keys2 = map { getkey2($_) } @unsorted;
    right:
     for (@unsorted) {
         push(@keys1, getkey1($_));
         push(@keys2, getkey2($_));
     }
理由はみなさんおわかりですね。

-------

いや、いや、ちょっと待って! 対照実験ではありません!

mapよりforeachの方がたいてい速い; その上、信じがたいことだが、
理屈は至極だが、時々リストを二回繰り返す方が一回より速いです。
(速くない時、差が最小限です)

例えば:

----

bash-2.02$ perl testit.pl
Benchmark: timing 100 iterations of for_once, for_twice, hash, map_once,
map_twi
ce...
  for_once:  3 wallclock secs ( 3.44 usr +  0.02 sys =  3.46 CPU)
 for_twice:  3 wallclock secs ( 3.37 usr +  0.04 sys =  3.41 CPU)
      hash:  4 wallclock secs ( 3.92 usr +  0.00 sys =  3.92 CPU)
  map_once:  6 wallclock secs ( 5.38 usr +  0.00 sys =  5.38 CPU)
 map_twice:  4 wallclock secs ( 4.66 usr +  0.00 sys =  4.66 CPU)

------

これはもうすこしフェアな実験だと思います:

For 5000 elements...
Benchmark: timing 500 iterations of for_loop_once, for_loop_twice,
for_sm_once, for_sm_twice, hash, map_once, map_twice...
for_loop_once: 48 wallclock secs (48.64 usr +  0.00 sys = 48.64 CPU)
for_loop_twice: 52 wallclock secs (51.20 usr +  0.00 sys = 51.20 CPU)
for_sm_once: 47 wallclock secs (47.43 usr +  0.00 sys = 47.43 CPU)
for_sm_twice: 49 wallclock secs (49.28 usr +  0.00 sys = 49.28 CPU)
      hash: 48 wallclock secs (48.05 usr +  0.00 sys = 48.05 CPU)
  map_once: 182 wallclock secs (181.64 usr +  0.00 sys = 181.64 CPU)
 map_twice: 66 wallclock secs (65.96 usr +  0.00 sys = 65.96 CPU)

ついてながら、hashの方をstatement modifierのような形になると、
一番速いと思います。

use Benchmark;

open(TEST, "control.tex"); { undef $/; $_=<TEST>; } close TEST;
@unsorted=(split)[1..5000]; # Just some random words.

print "For ",scalar @unsorted," elements...\n";
timethese(500,
     {
          map_twice => sub {
              my (@keys1, @ keys2); # Make sure we've a clean slate.
              @keys1 = map { getkey1($_) } @unsorted;
              @keys2 = map { getkey2($_) } @unsorted;
          },
          map_once => sub {
               my (@keys1, @ keys2);
               map {push(@keys1, getkey1($_)),
                    push(@keys2,getkey2($_))
               } @unsorted;
          },
          for_sm_twice => sub { # sm == statement modifier
               my (@keys1, @ keys2);
               push @keys1, getkey1($_) for @unsorted;
               push @keys2, getkey2($_) for @unsorted;
          },
          for_loop_twice => sub {
               my (@keys1, @ keys2);
               for (@unsorted) { push @keys1, getkey1($_) };
               for (@unsorted) { push @keys2, getkey2($_) };
          },
          for_loop_once => sub {
               my (@keys1, @ keys2);
               foreach(@unsorted) {
                    push @keys1, getkey1($_);
                    push @keys2, getkey2($_);
               }
          },
          for_sm_once => sub {
               my (@keys1, @ keys2);
               (push(@keys1,getkey1($_)),
               push(@keys2,getkey2($_))) for @unsorted;
          },
          hash => sub { # Cute and fast.
               my %hash;
               my (@keys1, @ keys2);
               foreach(@unsorted) {
                    $hash[getkey1($_)]=getkey2($_);
               };

# Try "$hash[getkey1($_)]=getkey2($_) for @unsorted" for more fun

               @keys1=keys %hash; @keys2=values %hash;
          }
     }
);

# Just some dummy functions
sub getkey1 { return reverse shift() }
sub getkey2 { return shift()."foo" }

# Simon

--
Japanese And Perl Hacker.





Tokyo-pm メーリングリストの案内