I played with this for entirely too long and got about a 8% improvement on tilly's according to Benchmark (on linux with an old Pentium M). This was mostly by changing $tile_index to a counter instead of finding it by addition each time. I also made a lookup table for bx/byScaled but this did little.

While doing this I got to wondering about the code snippet at the end:

my $tile = $tiles[$rx][$ry][$bz][type]; if (!defined $tile || $$tile != $type_data[$tile_index]) { $changed = 1; $$tile = $type_data[$tile_index]; }

which I don't understand. If $tile is undefined where does $$tile go to? If $$tile goes nowhere how come there's no warning or anything?

I combined everyone's code together in one large benchmark:

use strict; use warnings; use Benchmark qw(cmpthese); use constant type => 0; cmpthese (-1, { Original => 'org (0, 0, 0)', Grandfathers => 'gf (0, 0, 0)', Tillys => 'tilly (0, 0, 0)', justers => 'juster (0, 0, 0)' }); sub org { my ($bz, $bx, $by) = @_; my $changed; my @tiles; my @type_data; for my $y (0 .. 15) { for my $x (0 .. 15) { # this calculates the tile index we are currently at, # using the x and y coords in this block my $tile_index = $y + ($x * 16); # this calculates the real x and y values of this tile my $rx = ($bx * 16) + $x; my $ry = ($by * 16) + $y; # insert type data into the internal map array and # note that there was change in this block if applicable if (!defined $tiles[$rx][$ry][$bz][type] || $tiles[$rx][$ry][$bz][type] != $type_data[$tile_ind +ex]) { $changed = 1; $tiles[$rx][$ry][$bz][type] = $type_data[$tile_index]; } } } } sub gf { my ($bz, $bx, $by) = @_; my $changed; my @tiles; my @type_data; my $bxScaled = $bx * 16; my $byScaled = $by * 16; for my $x (0 .. 15) { my $xScaled = 16 * $x; for my $y (0 .. 15) { # this calculates the tile index we are currently at, # using the x and y coords in this block my $tile_index = $y + $xScaled; # this calculates the real x and y values of this tile my $rx = $bxScaled + $x; my $ry = $byScaled + $y; # insert type data into the internal map array and # note that there was change in this block if applicable my $tile = $tiles[$rx][$ry][$bz][type]; if (!defined $tile || $$tile != $type_data[$tile_index]) { $changed = 1; $$tile = $type_data[$tile_index]; } } } } sub tilly { my ($bz, $bx, $by) = @_; my $changed; my @tiles; my @type_data; my $bxScaled = $bx * 16; my $byScaled = $by * 16; for my $x (0 .. 15) { my $xScaled = 16 * $x; # These calculations were factored out of the inner loop my $rx = $bxScaled + $x; my $tiles_for_rx = $tiles[$rx]; for my $y (0 .. 15) { # this calculates the tile index we are currently at, # using the x and y coords in this block my $tile_index = $y + $xScaled; # this calculates the real y value of this tile my $ry = $byScaled + $y; # insert type data into the internal map array and # note that there was change in this block if applicable my $tile = $tiles_for_rx->[$ry][$bz][type]; if (!defined $tile || $$tile != $type_data[$tile_index]) { $changed = 1; $$tile = $type_data[$tile_index]; } } } } sub juster { my ($bz, $bx, $by) = @_; my $changed; my @tiles; my @type_data; my $bxScaledBase = $bx * 16; my $byScaledBase = $by * 16; # seemed to help a bit! my $tile_index=0; # this didn't help much my (@bxScaled, @byScaled); for (0..15) { push @byScaled, $byScaledBase+$_; push @bxScaled, $bxScaledBase+$_; } for my $x (0 .. 15) { my $xScaled = 16 * $x; # These calculations were factored out of the inner loop my $rx = $bxScaled[$x]; my $tiles_for_rx = $tiles[$rx]; for my $y (0 .. 15) { # this calculates the real y value of this tile my $ry = $byScaled[$y]; # insert type data into the internal map array and # note that there was change in this block if applicable my $tile = $tiles_for_rx->[$ry][$bz][type]; if (!defined $tile || $$tile != $type_data[$tile_index]) { $changed = 1; $$tile = $type_data[$tile_index]; } # I think this works properly, but ruins the benchmark unless # applied to gf's and tillys. # if (!exists $tiles_for_rx->[$ry][$bz][type] || # $tiles_for_rx->[$ry][$bz][type] != $type_data[$tile_i +ndex]) # { # $changed = 1; # $tiles_for_rx->[$ry][$bz][type] = $type_data[$tile_ +index]; # } ++$tile_index; } } }

Thanks for the introduction to Benchmark and to the OP for an interesting node.

Update: after looking at the code I posted I may have screwed up the order of comparisons and I'm too tired to fix it... but the point was... using an iterator.

You could even have the x,y,z stored linearly in an array which would reduce the loops and have the same results. Then you could have an array of arrays (because you need a sub-array for the type element right?) and just iterate over with one loop instead of two.

IE a matrix like this:

(1 2 3) (4 5 6) ===> ( 1 2 3 4 5 6 7 8 9 ) (7 8 9) how to do in three dimensions, I dunno...

In reply to Re^2: How to speed up a nested loop? by juster
in thread How to speed up a nested loop? by Xenofur

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.