Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl6 vs Perl5 benchmarking using prime numbers

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.

like image 783
petre Avatar asked Apr 02 '18 08:04

petre


1 Answers

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.

like image 196
Elizabeth Mattijsen Avatar answered Sep 27 '22 15:09

Elizabeth Mattijsen