This is a discussion moved from this HN thread here, regarding benchmarking Perl6 vs. Perl5 vs. other languages using an algorithm which implements the Sieve of Sundaram for finding prime numbers.
Here's original code in the original thread:
perl5 0m0.156s
perl6 0m6.615s
The problem is that the Perl6 version takes too long to find primes, compared to the Perl5 implementation. Part of it is due to using float as input, but still, it's too slow.
The objective is not necessarily to optimize the algorithm, but rather to identify why Perl6 is so slow compared to other languages.
Turns out that even though primes are integers, in your Perl 6 version, every calculation was done by using floating point. And this was caused by the call to the subroutine. If you would do:
sieve_sundaram(1000)
instead of:
sieve_sundaram(1e3)
then it all of a sudden becomes 4x as fast. In Perl 5 you never know what you're dealing with with regards to values. In Perl 6 if you tell it to use a floating point, it will infect all calculations to be in floating point afterwards. 1e3
is a floating point value. 1000
is an integer in Perl 6.
Also, you seem to have a sub-optimal algorithm: the second foreach
doesn't need to go from 1..$n
, but can go from $i..$n
instead. This brings down the runtime of the Perl 5 version of the code to 89 msecs for me.
Since your program is not using BigInt in the Perl 5 version, it is basically using native integers. In Perl 6, all integer calculations are always BigInts, unless you mark them as native. If I adjust your Perl 6 version for this, the runtime goes down from 4671 msecs to 414 msecs for this version:
sub sieve_sundaram(int $n) {
my %a;
my int @s = 2;
my int $m = $n div 2 - 1;
for 1..$n -> int $i {
for $i..$n -> int $j {
my int $p = $i + $j + 2 * $i * $j;
if $p < $m {
%a{$p} = True;
}
}
}
for 1..$m -> int $k {
if ! %a{$k} {
my int $q = 2 * $k + 1;
@s.push($q);
}
}
return @s;
}
sieve_sundaram(1000);
So, about 11x faster than before. And just under 5x as slow as the Perl 5 version.
The most idiomatic version for getting primes in Perl 6 is:
(1..1000).grep( *.is-prime )
Which for me executes within noise of your original Perl 5 algorithm. For larger values on multi-CPU machines, I would write this as:
(1..2500).hyper.grep( *.is-prime )
Around 2500 it becomes faster to hyper
it, so the work gets automatically distributed over multiple CPU's.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With