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
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |