in reply to Looping backwards through two arrays at once?

A note on performance:

Two optimizations are needed for "Perl-style" for my $i (reverse 0..$#a) to be as fast as the "C-style" for loop: 1) The elimination of reverse by processing the list backwards, and 2) the treatment of 0..$#a as an interator instead of flattening it. Not all versions of perl implement both of these implementations. As such, it's slower in some versions. (Update: 5.8.6 added (1). (2) still missing when reverse is used. c.f. Re^4: the '..' operator and decreasing values)

use strict; use warnings; use Benchmark qw( cmpthese ); my @a = map { int rand 10 } 1..100; my @b = map { int rand 10 } 1..100; sub perl_for { my $dummy_load; for my $i (reverse 0..$#a) { $dummy_load = $a[$i] . ' ' . $b[$i]; } 1; } sub c_style_for { my $dummy_load; for (my $i=@a; $i--; ) { $dummy_load = $a[$i] . ' ' . $b[$i]; } 1; } sub while_loop { my $dummy_load; my $i = @a; while ($i--) { $dummy_load = $a[$i] . ' ' . $b[$i]; } 1; } sub ickyb0d { my $dummy_load; for my $i (0..$#a) { $dummy_load = $a[$#a-$i] . ' ' . $b[$#a-$i]; } 1; } sub ickyb0d_ { my $dummy_load; for (0..$#a) { my $i = $#a-$_; $dummy_load = $a[$i] . ' ' . $b[$i]; } 1; } sub diotalevi { my $dummy_load; for my $i (1..@a) { $dummy_load = $a[-$i] . ' ' . $b[-$i]; } 1; } cmpthese(-3, { perl_for => \&perl_for, c_style_for => \&c_style_for, while_loop => \&while_loop, ickyb0d => \&ickyb0d, ickyb0d_ => \&ickyb0d_, diotalevi => \&diotalevi, });
This is perl, v5.6.1 built for MSWin32-x86-multi-thread Rate ickyb0d ickyb0d_ perl_for diotalevi while_loop c +_style_for ickyb0d 2516/s -- -8% -44% -48% -52% + -52% ickyb0d_ 2726/s 8% -- -40% -43% -48% + -48% perl_for 4507/s 79% 65% -- -6% -14% + -14% diotalevi 4794/s 91% 76% 6% -- -9% + -9% while_loop 5261/s 109% 93% 17% 10% -- + -0% c_style_for 5261/s 109% 93% 17% 10% 0% + --

Still, the Perl-style for loop is not much slower (14% slower, maybe less for smaller arrays) than the C-style for loop even in "old" version 5.6.1.

Update: Added diotalevi's solution.

Replies are listed 'Best First'.
Re^2: Looping backwards through two arrays at once?
by diotalevi (Canon) on Nov 04, 2005 at 17:40 UTC

    I wasn't aware of for ( reverse N .. M ) being optimized in *any* case. Because of that, I'd normally write that without the reverse() and use the number as an offset from the end. In the loop, treating the offset as a negative number works to get the "from the end" effect. This buys the iterator space savings of for(N..M) back.

    for my $offset ( 0 .. $#ary_a ) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }
      for my $offset ( 0 .. $#ary_a ) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }

      This is the reply I was going to give but you're suffering from an off-by-one error. It should be:

      for my $offset (1..@ary_a) { foobar( $ary_a[ -$offset ], $ary_b[ -$offset ] ); }