in reply to Syntactic sugar for tail call optimizations

I frequently use redo as a more readable version of do/while. But in my off-the-cuff test using your functions, f1 is always about 50% faster than f2. I tested using perls 5.10 .. 5.14.
use Benchmark qw(cmpthese); my ($a, $b) = (0,0); my $limit = 10_000; sub f1 { ++$a < $limit ? goto &f1 : return $a} sub f2 {{ ++$b < $limit ? redo : return $b }} cmpthese -1, { f1 => \&f1, f2 => \&f2 };

Replies are listed 'Best First'.
Re^2: Syntactic sugar for tail call optimizations
by ikegami (Patriarch) on May 27, 2011 at 03:01 UTC

    Bad benchmark. Out of the millions of passes of each sub, you only count up to 10,000 once. The only relevant result is dropped as an outlier, so you're benchmarking inc+compare+return vs loop+inc+compare+return. Of course the latter will be slower.

    Also, you should probably include the naïve case.

    use Benchmark qw(cmpthese); my $limit = 10_000; sub f0 { ++$_ < $limit ? &f1 : return $_ } sub f1 { ++$_ < $limit ? goto &f1 : return $_ } sub f2 {{ ++$_ < $limit ? redo : return $_ }} cmpthese -1, { f0 => sub { $_ = 0; f0() }, f1 => sub { $_ = 0; f1() }, f2 => sub { $_ = 0; f2() }, };
    Rate f1 f0 f2 f1 245/s -- -15% -71% f0 288/s 18% -- -65% f2 836/s 241% 190% -- Rate f1 f0 f2 f1 212/s -- -22% -78% f0 271/s 28% -- -72% f2 954/s 350% 253% -- Rate f1 f0 f2 f1 218/s -- -23% -77% f0 283/s 30% -- -70% f2 932/s 328% 229% --
      Thanks!

      I just wanted to point out that there other reasons than speed to use tail calls.

      But after spotting and correcting the typo you had in in f0() it becomes more evident

      use Benchmark qw(cmpthese); use diagnostics; my $limit = 10_000; sub f0 { ++$_ < $limit ? &f0 : return $_ } sub f1 { ++$_ < $limit ? goto &f1 : return $_ } sub f2 {{ ++$_ < $limit ? redo : return $_ }} cmpthese -1, { f0 => sub { $_ = 0; f0() }, f1 => sub { $_ = 0; f1() }, f2 => sub { $_ = 0; f2() }, };

      Deep recursion on subroutine "main::f0" at /home/lanx/B/PL/PM/tail_cal +l.pl line 6 (#1) (W recursion) This subroutine has called itself (directly or indir +ectly) 100 times more than it has returned. This probably indicates an infinite recursion, unless you're writing strange benchmark progra +ms, in which case it indicates something else. Rate f1 f0 f2 f1 76.1/s -- -31% -73% f0 111/s 46% -- -60% f2 280/s 267% 152% --

      Tail calls reuse the current call frame and thus avoid stack overflows in deep recursions.

      I was able to make my system heavily swapping and unresponsive with a limit of 1000000.

      Cheers Rolf

        Tail calls reuse the current call frame and thus avoid stack overflows in deep recursions.

        There is no stack to overflow since the frames are on the heap.