Skip to content

Commit e0c5fec

Browse files
authored
PDL extensions (PlummersSoftwareLLC#776)
1 parent 860048f commit e0c5fec

File tree

6 files changed

+214
-17
lines changed

6 files changed

+214
-17
lines changed

PrimePDL/solution_1/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM wlmb/perldatalanguage:v7
1+
FROM wlmb/perldatalanguage:v8
22

33
WORKDIR /opt/app
44
COPY *.pl .

PrimePDL/solution_1/README.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
![Algorithm](https://img.shields.io/badge/Algorithm-base-green)
44
![Faithfulness](https://img.shields.io/badge/Faithful-yes-green)
55
![Parallelism](https://img.shields.io/badge/Parallel-no-green)
6-
![Bit count](https://img.shields.io/badge/Bits-unknown-yellowgreen)
6+
![Bit count](https://img.shields.io/badge/Bits-8-yellowgreen)
77

88
My implementation uses the Perl Data Language (PDL) to mark all multiples of
99
a prime with a single instruction. PDL is a package that extends Perl,
@@ -37,27 +37,27 @@ docker run --rm primes
3737
## Output
3838

3939
```
40-
Luis_Mochán_(wlmb)_Perl/PDL;440;5.002161;1;algorithm=base,faithful=yes
41-
Luis_Mochán_(wlmb)_Perl/PDL;459;5.006921;1;algorithm=base,faithful=yes
42-
Luis_Mochán_(wlmb)_Perl/PDL;461;5.009201;1;algorithm=base,faithful=yes
43-
Luis_Mochán_(wlmb)_Perl/PDL;456;5.007696;1;algorithm=base,faithful=yes
44-
Luis_Mochán_(wlmb)_Perl/PDL;463;5.000091;1;algorithm=base,faithful=yes
40+
Luis_Mochán_(wlmb)_Perl/PDL;726;5.001482;1;algorithm=base,faithful=yes,bits=8
41+
Luis_Mochán_(wlmb)_Perl/PDL;723;5.002705;1;algorithm=base,faithful=yes,bits=8
42+
Luis_Mochán_(wlmb)_Perl/PDL;721;5.003962;1;algorithm=base,faithful=yes,bits=8
43+
Luis_Mochán_(wlmb)_Perl/PDL;731;5.004047;1;algorithm=base,faithful=yes,bits=8
44+
Luis_Mochán_(wlmb)_Perl/PDL;714;5.005479;1;algorithm=base,faithful=yes,bits=8
4545
```
4646

4747
### Benchmarks
4848

49-
In my laptop it's about an order of magnitude faster than Perl's
49+
In my laptop it's about twenty times faster than Perl's
5050
solution 1. I did use external modules, but they are general purpose
5151
language extensions, not targeted to this problem.
5252

5353
On my Dell Latitude E7450, I get the following numbers:
5454

5555
```
56-
Passes: 459, Time: 5.00613212585449, Per pass: 0.0109066059386808 Limit: 1000000 Count: 78498 Valid: 1
57-
Passes: 442, Time: 5.00916695594788, Per pass: 0.0113329569139092 Limit: 1000000 Count: 78498 Valid: 1
58-
Passes: 441, Time: 5.00524806976318, Per pass: 0.0113497688656762 Limit: 1000000 Count: 78498 Valid: 1
59-
Passes: 456, Time: 5.00585103034973, Per pass: 0.0109777434876091 Limit: 1000000 Count: 78498 Valid: 1
60-
Passes: 448, Time: 5.00958800315857, Per pass: 0.0111821160784789 Limit: 1000000 Count: 78498 Valid: 1
56+
Passes: 726, Time: 5.0014820098877, Per pass: 0.00688909367753126 Limit: 1000000 Count: 78498 Valid: 1
57+
Passes: 723, Time: 5.00270485877991, Per pass: 0.00691937048240651 Limit: 1000000 Count: 78498 Valid: 1
58+
Passes: 721, Time: 5.00396203994751, Per pass: 0.00694030796109225 Limit: 1000000 Count: 78498 Valid: 1
59+
Passes: 731, Time: 5.00404691696167, Per pass: 0.00684548141855222 Limit: 1000000 Count: 78498 Valid: 1
60+
Passes: 714, Time: 5.00547909736633, Per pass: 0.00701047492628338 Limit: 1000000 Count: 78498 Valid: 1
6161
```
6262

6363
## Author

PrimePDL/solution_1/primes.pl

+5-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ package PrimeSieve;
2222
sub new {
2323
my ( $class, $sieve_size ) = @_;
2424
# die "Expect a positive power of ten" unless (0+$sieve_size) =~ /^10+$/;
25-
my $bits=zeroes($sieve_size+1);
25+
my $bits=zeroes(byte, $sieve_size+1);
2626
my $q=sqrt($sieve_size);
2727
$bits(0:1).=1; # 0 and 1 are not prime
2828
return bless {
@@ -39,16 +39,17 @@ sub run_sieve {
3939
return if $self->{ran};
4040
my $q = $self->{q};
4141
my $bits = $self->{bits};
42+
my $one=pdl(byte, 1);
4243
for(my $factor=3; $factor<=$q; $factor+=2) {
43-
$bits($factor*$factor:-1:2*$factor).=1 unless $bits(($factor));
44+
$bits($factor*$factor:-1:2*$factor).=$one unless $bits(($factor));
4445
}
4546
$self->{ran}=1;
4647
}
4748

4849
sub print_results {
4950
my ( $self, $show_primes, $show_stats, $duration, $passes ) = @_;
5051
say $self->get_primes if $show_primes;
51-
printf "Luis_Mochán_(wlmb)_Perl/PDL;%d;%f;%d;algorithm=base,faithful=yes\n",
52+
printf "Luis_Mochán_(wlmb)_Perl/PDL;%d;%f;%d;algorithm=base,faithful=yes,bits=8\n",
5253
$passes, $duration, 1;
5354
say "Passes: $passes, Time: $duration, Per pass: ", $duration/$passes,
5455
" Limit: ", $self->{sieve_size}, " Count: ", $self->count_primes,
@@ -66,7 +67,7 @@ sub get_primes {
6667
my $self = shift;
6768
$self->deal_with_even;
6869
my $bits=$self->{bits};
69-
$bits->xvals->where(!$bits);
70+
$bits->long->xvals->where(!$bits);
7071
}
7172

7273
sub count_primes {

PrimePDL/solution_2/Dockerfile

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM wlmb/perldatalanguage:v8
2+
3+
WORKDIR /opt/app
4+
COPY *.pl .
5+
6+
ENTRYPOINT [ "perl", "primes.pl" ]

PrimePDL/solution_2/README.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Perl solution using PDL by Luis Mochan
2+
3+
![Algorithm](https://img.shields.io/badge/Algorithm-base-green)
4+
![Faithfulness](https://img.shields.io/badge/Faithful-yes-green)
5+
![Parallelism](https://img.shields.io/badge/Parallel-no-green)
6+
![Bit count](https://img.shields.io/badge/Bits-8-yellowgreen)
7+
8+
My implementation uses the Perl Data Language (PDL). PDL is a package that extends Perl,
9+
allowing relatively fast number-crunching using objects that contain
10+
C-like arrays without loosing Perl's expressiveness. For this solution
11+
I use its pp_def mechanism that allows defining new threadable routines
12+
using a C-like syntax and allowing them to be called from Perl. This
13+
mechanism is used to define most of the PDL extensions of Perl, but it
14+
can also be employed by the language users.
15+
16+
17+
## Run
18+
19+
### Running locally
20+
21+
Perl usually comes preinstalled on a Linux system; if it's missing
22+
from your machine, check your package manager. For Windows, there's
23+
ActivePerl or Strawberry Perl. If PDL is also installed, then the
24+
program may by run as
25+
26+
```
27+
perl primes.pl
28+
```
29+
30+
### Docker
31+
32+
No local Perl or PDL? Just run it in Docker.
33+
34+
```
35+
docker build -t primes .
36+
docker run --rm primes
37+
```
38+
39+
## Output
40+
41+
```
42+
Luis_Mochán_(wlmb)_Perl/PDL-PP;1091;5.003844;1;algorithm=base,faithful=yes,bits=8
43+
Luis_Mochán_(wlmb)_Perl/PDL-PP;1063;5.004238;1;algorithm=base,faithful=yes,bits=8
44+
Luis_Mochán_(wlmb)_Perl/PDL-PP;1067;5.003807;1;algorithm=base,faithful=yes,bits=8
45+
Luis_Mochán_(wlmb)_Perl/PDL-PP;1065;5.000665;1;algorithm=base,faithful=yes,bits=8
46+
Luis_Mochán_(wlmb)_Perl/PDL-PP;1043;5.001787;1;algorithm=base,faithful=yes,bits=8
47+
```
48+
49+
### Benchmarks
50+
51+
In my laptop it's more than thirty times faster than Perl's
52+
solution 1. I did use external modules, but they are general purpose
53+
language extensions, not targeted to this problem. I used the PDL's pp_def
54+
mechanism to write threadable C-like functions.
55+
56+
On my Dell Latitude E7450, I get the following numbers:
57+
58+
```
59+
Passes: 1091, Time: 5.0038, Per pass: 4.586e-03 Limit: 1000000, Count: 78498, Valid: 1
60+
Passes: 1063, Time: 5.0042, Per pass: 4.708e-03 Limit: 1000000, Count: 78498, Valid: 1
61+
Passes: 1067, Time: 5.0038, Per pass: 4.690e-03 Limit: 1000000, Count: 78498, Valid: 1
62+
Passes: 1065, Time: 5.0007, Per pass: 4.695e-03 Limit: 1000000, Count: 78498, Valid: 1
63+
Passes: 1043, Time: 5.0018, Per pass: 4.796e-03 Limit: 1000000, Count: 78498, Valid: 1
64+
```
65+
66+
## Author
67+
68+
Luis Mochán

PrimePDL/solution_2/primes.pl

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env perl
2+
package PrimeSieve;
3+
use v5.12;
4+
use strict;
5+
use warnings;
6+
use PDL;
7+
use PDL::NiceSlice;
8+
set_autopthread_targ(1);
9+
my $DEBUG=0;
10+
my %primes_lower_than = (
11+
10 => 4,
12+
100 => 25,
13+
1000 => 168,
14+
10000 => 1229,
15+
100000 => 9592,
16+
1000000 => 78498,
17+
10000000 => 664579,
18+
100000000 => 5761455,
19+
1000000000 => 50847534,
20+
10000000000 => 455052511,
21+
);
22+
23+
sub new {
24+
my ( $class, $sieve_size ) = @_;
25+
# die "Expect a positive power of ten" unless (0+$sieve_size) =~ /^10+$/;
26+
my $bits=zeroes(byte, $sieve_size+1);
27+
my $q=sqrt($sieve_size);
28+
$bits(0:1).=1; # 0 and 1 are not prime
29+
return bless {
30+
sieve_size => $sieve_size,
31+
bits => $bits,
32+
q => $q,
33+
even => 0,
34+
ran => 0
35+
}, $class;
36+
}
37+
38+
sub run_sieve {
39+
my $self = shift;
40+
return if $self->{ran};
41+
my $q = $self->{q};
42+
my $bits = $self->{bits};
43+
my $factors=zeroes(long,$q+1-3)->xvals*2+3;
44+
say $factors if $DEBUG;
45+
$bits->run_sieve_aux($factors);
46+
}
47+
48+
sub print_results {
49+
my ( $self, $show_primes, $show_stats, $duration, $passes ) = @_;
50+
say $self->get_primes if $show_primes;
51+
printf "Luis_Mochán_(wlmb)_Perl/PDL-PP;%d;%f;%d;algorithm=base,faithful=yes,bits=8\n",
52+
$passes, $duration, 1;
53+
say sprintf "Passes: %d, Time: %.4f, Per pass: %.3e Limit: %d, Count: %d, Valid: %d",
54+
$passes, $duration, $duration/$passes, $self->{sieve_size}, $self->count_primes,
55+
$self->validate_results
56+
if $show_stats;
57+
}
58+
59+
sub deal_with_even {
60+
my $self = shift;
61+
my $bits=$self->{bits};
62+
$bits(2*2:-1:2).=1 unless $self->{even};
63+
$self->{even}=1;
64+
}
65+
66+
sub get_primes {
67+
my $self = shift;
68+
$self->deal_with_even;
69+
my $bits=$self->{bits};
70+
$bits->long->xvals->where(!$bits);
71+
}
72+
73+
sub count_primes {
74+
my $self = shift;
75+
$self->deal_with_even;
76+
my $bits=$self->{bits};
77+
(!$bits)->sumover;
78+
}
79+
80+
sub validate_results {
81+
my $self = shift;
82+
return ( $primes_lower_than{ $self->{sieve_size} } == $self->count_primes() );
83+
}
84+
85+
no PDL::NiceSlice;
86+
use Inline Pdlpp => <<'EOPP';
87+
pp_def('run_sieve_aux',
88+
Pars=>'[io]bits(n);factors(q);',
89+
Code=>q{
90+
int f, i, f2, ff, sn;
91+
loop(q) %{
92+
f=$factors();
93+
if($bits(n=>f)==0){
94+
f2=2*f;
95+
ff=f*f;
96+
sn=$SIZE(n);
97+
for(i=ff; i < sn; i+=f2){
98+
$bits(n=>i)=1;
99+
}
100+
}
101+
%}
102+
},
103+
);
104+
EOPP
105+
106+
107+
package main;
108+
use Time::HiRes qw(time);
109+
my $size=1_000_000;
110+
my $passes = 0;
111+
my $duration=0;
112+
my $sieve;
113+
my $start_time = time;
114+
while ($duration<5) {
115+
$sieve = PrimeSieve->new($size);
116+
$sieve->run_sieve();
117+
$passes++;
118+
$duration = time - $start_time;
119+
}
120+
$sieve->print_results( 0, 0, $duration, $passes );
121+
122+
__END__

0 commit comments

Comments
 (0)