in reply to Unbelievably slow..

I don't know how much this will help your specific situation, but you'll generally see an improvement if you write Perl like Perl, not as a dynamically-typed C.

The thing that immediately stands out to me is the use of the three-arg form of the for loop in Perl. This is basically there for the benifit of old C programmers. Personally, I see it used so seldom Perl that I often forget it exists.

The Perl-ism is something like this:

# Change this: for($num=0;$num<16777216;$num++) # To this: for my $num (0 .. 16777216) # And this: for($i=1;$i<=24;$i++) # Becomes this: for my $i (1 .. 24)

Using more Perl-ish constructs often provides hints to the optimizer, as well as generally reducing code size without hurting maintainability.

A few other ideas that may or may not help:

----
I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
-- Schemer

Note: All code is untested, unless otherwise stated

Replies are listed 'Best First'.
Re: Re: Believably slow..
by diotalevi (Canon) on Jun 22, 2003 at 14:50 UTC

    Switching to an iterator-style for from the C-style for, using lexicals instead of globals reduces my running time from ten minutes thirty seconds to seven minutes thirty seconds. If you weren't aware the C-style for is equivalent to the pseudo-code initializer; while( condition ) { ... ; post_loop_action } which involves more steps than a .. iterator. In my example I moved the my() parts out of the tight loop because they have runtime implications. I used lexicals over globals because its one opcode lighter (a very, very cheap opcode though). Switching to 'use integer' further reduced the runtime to nine minutes forty five seconds (did I mention I'm doing this on an otherwise unburdoned machine?) and six minutes respectively.

    The net effect is this particular form of "optimization" didn't buy me all that much but just shows that perl doesn't compete with simple things like that C++ which fits nicely within the processor's cache and is just a few instructions. The equivalent perl code is significantly more complex and of course takes longer. I wouldn't be surprised if most of the example's compiled C++ code was mostly register operations.

    # The optimized perl code use strict; use warnings FATAL => 'all'; use integer; my $ans = 0; my $total; my $shift; for my $num ( 0 .. 16777216 ) { $total = 0; $shift = 1; for my $i ( 1 .. 24 ) { $total += $num & $shift ? $i : -$i; $shift <<= 1; } $ans++ if $total == 0; } print "$ans\n";
      I guess you can look at it this way, your task is simular to making a round object that will roll down a hill, C lets you make just a simple wheel with no other baggage, Perl lets you make a wheel but gives you all the baggage that comes along with making the rest of a car. There is overhead to all of the memory managment, hash tables, etc that comes with even a one liner perl script -- stright C code can avoid most of this if it is not needed. On the other hand your C app as it grows into the "car" where it becomes more complecated will start to perform much more like the perl script overall.

      -Waswas

        Of course. Perl is a C application and is only slower and bigger because it must be so flexible. As a C application becomes more flexible there's a point at which it starts working its way into perl's territory.

      I've been trying your optimized code on my machine (an old Duron 700MHz, under medium load):
      $ time ./bench.pl
      187692
      
      real    17m12.507s
      user    10m3.640s
      sys     0m1.300s
      

      so it's still very slow compared to what it does.
      Then, I've tried to compile it:
      $ perlcc -O -o bench bench.pl
      $ time ./bench
      187692
      
      real    12m35.665s
      user    6m1.480s
      sys     0m0.720s
      

      It still is definitely slow, but you get an evident boost in performance just by compiling it into C code... something to remember for situations in which you really need Perl for doing lengthy tasks.

        Well... at that point you should be using Inline::C. You write just that one function in C and leave the rest in C. You should not be under the illusion that perlcc is a substitute for that. In fact, Inline::C is one of the premier ways to interface with C stuff these days. You'd probably even want to pick it over XS when possible.

Re: Re: Believably slow..
by BazB (Priest) on Jun 22, 2003 at 14:05 UTC
    use integer; rather than use int;.

    If the information in this post is inaccurate, or just plain wrong, don't just downvote - please post explaining what's wrong.
    That way everyone learns.

Re: Re: Believably slow..
by PodMaster (Abbot) on Jun 22, 2003 at 14:45 UTC
    Not that it matters much ...
    use Benchmark 'cmpthese'; #16777216 cmpthese( 9000, #-3, { 'c-style' => sub { for my $num (0 .. 1000) {} return(); }, 'rangefor' => sub { for( my $num = 0; $num < 1000; $num++) {} return(); }, }); __END__ Benchmark: running c-style, rangefor, each for at least 3 CPU seconds. +.. c-style: 4 wallclock secs ( 3.20 usr + 0.00 sys = 3.20 CPU) @ 64 +94.54/s (n=20802) rangefor: 3 wallclock secs ( 3.27 usr + 0.00 sys = 3.27 CPU) @ 37 +93.02/s (n=12388) Rate rangefor c-style rangefor 3793/s -- -42% c-style 6495/s 71% -- Benchmark: timing 9000 iterations of c-style, rangefor... c-style: 1 wallclock secs ( 1.44 usr + 0.00 sys = 1.44 CPU) @ 62 +58.69/s (n=9000) rangefor: 3 wallclock secs ( 2.39 usr + 0.00 sys = 2.39 CPU) @ 37 +65.69/s (n=9000) Rate rangefor c-style rangefor 3766/s -- -40% c-style 6259/s 66% --
    update: lol

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      This benchmark would be far better if you had named the subroutines correctly. :-) Youll notice that you have the names for the two subs reversed.

      Also, I personally wouldn't do a benchmark with an empty block. Put something in it just in case Perl gets smart enough to optimize both of those subroutines down to sub{} and then maybe even just tosses any calls to them becuase they dont really do anything....

      use Benchmark 'cmpthese'; #16777216 my @num=0..1000; my ($x,$y,$z); my $subhash={ 'rangefor' => sub { for my $num (0 .. 1000) { $x++ } return(); }, 'c-style' => sub { for( my $num = 0; $num < 1000; $num++) { $y++ } return(); }, 'foreach' => sub { foreach my $num (@num) { $z++ } return(); }, }; cmpthese(-3,$subhash) for 1..3; __END__ Benchmark: running c-style, foreach, rangefor, each for at least 3 CPU + secs... c-style: 3 wall secs ( 3.25 usr + 0 sys = 3.25 CPU) @3039.64/s (n +=9891) foreach: 3 wall secs ( 3.30 usr + 0 sys = 3.30 CPU) @3930.39/s (n +=12986) rangefor: 3 wall secs ( 3.22 usr + 0 sys = 3.22 CPU) @4133.68/s (n +=13327) Rate c-style foreach rangefor c-style 3040/s -- -23% -26% foreach 3930/s 29% -- -5% rangefor 4134/s 36% 5% -- Benchmark: running c-style, foreach, rangefor, each for at least 3 CPU + secs... c-style: 3 wall secs ( 3.16 usr + 0 sys = 3.16 CPU) @3046.59/s (n +=9612) foreach: 3 wall secs ( 3.20 usr + 0 sys = 3.20 CPU) @3921.37/s (n +=12568) rangefor: 4 wall secs ( 3.18 usr + 0 sys = 3.18 CPU) @4080.31/s (n +=12955) Rate c-style foreach rangefor c-style 3047/s -- -22% -25% foreach 3921/s 29% -- -4% rangefor 4080/s 34% 4% -- Benchmark: running c-style, foreach, rangefor, each for at least 3 CPU + secs... c-style: 3 wall secs ( 3.14 usr + 0 sys = 3.14 CPU) @3037.84/s (n +=9554) foreach: 3 wall secs ( 3.30 usr + 0 sys = 3.30 CPU) @3941.12/s (n +=12986) rangefor: 3 wall secs ( 3.10 usr + 0 sys = 3.10 CPU) @4079.25/s (n +=12662) Rate c-style foreach rangefor c-style 3038/s -- -23% -26% foreach 3941/s 30% -- -3% rangefor 4079/s 34% 4% --

      ---
      demerphq

      <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...