
demerphq at gmail
Nov 3, 2009, 2:58 PM
Post #8 of 37
(661 views)
Permalink
|
2009/11/3 Tim Bunce <Tim.Bunce [at] pobox>: > On Mon, Nov 02, 2009 at 02:43:52PM +0100, demerphq wrote: >> 2009/11/2 Rafael Garcia-Suarez <rgs [at] consttype>: >> > 2009/11/2 demerphq <demerphq [at] gmail>: >> >> I have over the past months a number of times observed that eval is >> >> *deadly* slow. >> >> >> >> In particular using eval to load data structures is extremely slow. >> >> >> >> And the performance degrades substantially the more strings there are >> >> and the longer they are. >> >> >> >> Ive been thinking that perhaps we could really benefit by reexamining >> >> the string parsing logic to see if it is possible to speed it up, for >> >> instance the documentation claims we scan a string something like 3 >> >> times in order to parse it out. Perhaps we can cut down on this, or >> >> make it more efficient. >> >> >> >> Any thoughts? In particular does this sound worth while? >> > >> > That does, however, the code being what it is, that is, >> > euphemistically convoluted, I'd start with a profiling tool. I >> > wouldn't know where to start. >> >> I did some performance analysis of this, and it was pretty shocking. >> As the length of the strings increases the time it takes to compile >> degrades faster. So for instance double the length of the string and >> you more than double the time it takes to parse them (my mathemtically >> analysis skills arent strong enough for me to work out what the exact >> relationship is). And that is for strings with NO escapes or embedded >> constructs. > > Can you post some code to demonstrate? use strict; use warnings; use Benchmark qw(cmpthese timethese); my %all; for my $len (1,10,50,100,250,500,750,1000,5000,10000) { my @s; my $code1= "sub { \nmy \$out='';\n"; my $code2= "sub { \nmy \$o='';\n"; for (1..1000) { my $str="x" x $len; push @s, $str; $code1 .= qq( \$out .= "$str";\n); $code2 .= qq( \$o .= \$s[$#s];\n); } $_.= "}\n" for $code1,$code2; $code2=pack"(Z*)*",$code2,@s; my $subs= { "long" => sub { eval $code1 or die "code1 died: $@" }, "short" => sub { #my ($c,@s)=unpack"(Z*)*",$code2; my ($c,@s)=split /\0/, $code2; eval $c or die "code2 died: $@"; }, }; my @res; foreach my $sub (qw(short long)) { push @res, $subs->{$sub}->()->(); } use Data::Dumper; die Dumper(\@res) if !$res[0] or !$res[1] or $res[0] ne $res[1]; print "Timing length $len\n"; $all{$len}= timethese(-1,$subs); } foreach my $len (sort {$a <=> $b} keys %all) { my $t= $all{$len}; print join("\t", $len, map { sprintf "%.2f\t%d", $t->{$_}->iters / $t->{$_}->cpu_a, $t->{$_}->iters } qw(short long) ), "\n", ; } __END__ Timing length 1 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.03 usr + 0.00 sys = 1.03 CPU) @ 271.84/s (n=280) short: 1 wallclock secs ( 1.12 usr + 0.00 sys = 1.12 CPU) @ 214.29/s (n=240) Timing length 10 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.10 usr + 0.01 sys = 1.11 CPU) @ 252.25/s (n=280) short: 1 wallclock secs ( 1.12 usr + 0.01 sys = 1.13 CPU) @ 211.50/s (n=239) Timing length 50 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.12 usr + 0.00 sys = 1.12 CPU) @ 230.36/s (n=258) short: 2 wallclock secs ( 1.08 usr + 0.00 sys = 1.08 CPU) @ 193.52/s (n=209) Timing length 100 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.10 usr + 0.01 sys = 1.11 CPU) @ 200.90/s (n=223) short: 1 wallclock secs ( 1.13 usr + 0.01 sys = 1.14 CPU) @ 211.40/s (n=241) Timing length 250 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.10 usr + 0.00 sys = 1.10 CPU) @ 173.64/s (n=191) short: 1 wallclock secs ( 1.17 usr + 0.00 sys = 1.17 CPU) @ 178.63/s (n=209) Timing length 500 Benchmark: running long, short for at least 1 CPU seconds... long: 2 wallclock secs ( 1.10 usr + 0.01 sys = 1.11 CPU) @ 121.62/s (n=135) short: 1 wallclock secs ( 1.16 usr + 0.01 sys = 1.17 CPU) @ 150.43/s (n=176) Timing length 750 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 1.03 usr + 0.01 sys = 1.04 CPU) @ 89.42/s (n=93) short: 1 wallclock secs ( 1.04 usr + 0.00 sys = 1.04 CPU) @ 133.65/s (n=139) Timing length 1000 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 0.75 usr + 0.32 sys = 1.07 CPU) @ 45.79/s (n=49) short: 1 wallclock secs ( 1.04 usr + 0.12 sys = 1.16 CPU) @ 120.69/s (n=140) Timing length 5000 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 0.84 usr + 0.20 sys = 1.04 CPU) @ 15.38/s (n=16) short: 1 wallclock secs ( 0.87 usr + 0.19 sys = 1.06 CPU) @ 41.51/s (n=44) Timing length 10000 Benchmark: running long, short for at least 1 CPU seconds... long: 1 wallclock secs ( 0.81 usr + 0.20 sys = 1.01 CPU) @ 8.91/s (n=9) short: 1 wallclock secs ( 0.80 usr + 0.22 sys = 1.02 CPU) @ 26.47/s (n=27) 1 214.29 240 271.84 280 10 211.50 239 252.25 280 50 193.52 209 230.36 258 100 211.40 241 200.90 223 250 178.63 209 173.64 191 500 150.43 176 121.62 135 750 133.65 139 89.42 93 1000 120.69 140 45.79 49 5000 41.51 44 15.38 16 10000 26.47 27 8.91 9 -- perl -Mre=debug -e "/just|another|perl|hacker/"
|