Benchmarking while() vs for()
It starts with a tweet.
Just curious, which loop performs better 'while' or 'for' as far as #Perl is concerned?
— Mohammad S Anwar (@cpan_author) April 8, 2022
I have gut feeling, 'for' would win the race, right?
I thought about it for a while.
I haven't put them head-to-head.
— Dave (IND ✈️ LAS) (@JacobyDave) April 8, 2022
To me, it's purpose, not performance. If I know exactly how many things I'm looping, that's for. If it's "until done", that's while.
I think that bears further explanation. Classic for
loops look like:
for ( my $i = 0 ; $i < 20 ; $i++ ) { ... }
and that’s all fine, but to be honest, I hardly use that form. Most of my Perl uses of for
are much like
for my $i ( @array ) { ... }
and Perl handles iterating through the entries in the array. So, to me, it is purpose-built for static data sets. You don’t have to, though.
# this is a perfectly valid memory-sucking infinite loop
for my $i ( @array ) {
push @array, $i * 2;
}
while
, however, is built for when the size of the problem set is dynamic and unset. If you want to start now and keep going until it’s over, that’s when you pull out while
.
while ( @array ) {
shift @array;
}
But clearly, you can do my while
stuff in a for
loop, and mechanic ways to do for
stuff in a while loop. So let’s get back to Mohammad’s question. How do we determine what’s mroe performant?
Make a list of a billion, loop through it a billion times, compare times.
— Dave (IND ✈️ LAS) (@JacobyDave) April 8, 2022
Friends, this is where we go to Benchmark. In my case, I didn’t want to either wait forever or set fire to my laptop, so I went smaller numbers. Make a list of 1000, loop through it a million times. If you want to find the best (meaning most performant) way to do a thing, this is how we go.
#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw(:all);
my $count = 1_000_000;
timethese(
$count,
{
'ForLoop' => sub {
my @array = 1 .. 1000;
for (@array) {
shift @array;
}
},
'WhileLoop' => sub {
my @array = 1 .. 1000;
for (@array) {
shift @array;
}
},
}
);
The only difference is that one, we have an iterator going through an array, and one is getting a conditional. I ran this, made conclusions, but didn’t realize I wasn’t running for
vs while
but instead for
vs for
. I put it in a Gist and everything. SIlly Dave.
So, right now, I’m running it again, with slightly bigger numbers.
#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw(:all);
my $count = 10_000;
timethese(
$count,
{
'ForLoop' => sub {
my @array = 1 .. 10_000;
for (@array) {
shift @array;
}
},
'WhileLoop' => sub {
my @array = 1 .. 10_000;
while (@array) {
shift @array;
}
},
}
);
./forwhile.pl
Benchmark: timing 10000 iterations of ForLoop, WhileLoop...
ForLoop: 4 wallclock secs ( 2.69 usr + 0.02 sys = 2.71 CPU) @ 3690.04/s (n=10000)
WhileLoop: 5 wallclock secs ( 3.55 usr + 0.00 sys = 3.55 CPU) @ 2816.90/s (n=10000)
That’s a difference toward preferencing for
over while
, but again, I’m strongly for purposeful use. The numbers say WhileLoop is slightly slower, but the purpose I use them for makes it more understandable to me.