@@ -17,40 +17,30 @@ Date: 2022-07-10
17
17
*/
18
18
19
19
"use strict" ;
20
+ const NOW_UNITS_PER_SECOND = 1000 ;
21
+ const WORD_SIZE = 32 ;
20
22
23
+ let config = {
24
+ sieveSize : 1000000 ,
25
+ timeLimitSeconds : 5 ,
26
+ verbose : false ,
27
+ runtime : ''
28
+ } ;
21
29
22
- let runtime = "" ;
23
- let verbose = false ;
24
30
try
25
31
{
26
32
! ! Deno ;
27
- runtime = "deno" ;
28
- verbose = Deno . args . includes ( "verbose" ) ;
33
+ config . runtime = "deno" ;
34
+ config . verbose = Deno . args . includes ( "verbose" ) ;
29
35
}
30
36
catch
31
37
{
38
+ const { performance } = require ( 'perf_hooks' ) ;
32
39
const runtimeParts = process . argv [ 0 ] . split ( "/" ) ;
33
- runtime = runtimeParts [ runtimeParts . length - 1 ] ;
34
- verbose = process . argv . includes ( "verbose" ) ;
40
+ config . runtime = runtimeParts [ runtimeParts . length - 1 ] ;
41
+ config . verbose = process . argv . includes ( "verbose" ) ;
35
42
}
36
43
37
-
38
- const NOW_UNITS_PER_SECOND = 1000 ;
39
-
40
-
41
- // Historical data for validating our results - the number of primes
42
- // to be found under some limit, such as 168 primes under 1000
43
- const knownPrimeCounts = {
44
- 10 : 4 ,
45
- 100 : 25 ,
46
- 1000 : 168 ,
47
- 10000 : 1229 ,
48
- 100000 : 9592 ,
49
- 1000000 : 78498 ,
50
- 10000000 : 664579 ,
51
- 100000000 : 5761455
52
- } ;
53
-
54
44
// 32-bit bitarray for javascript, with only needed functions
55
45
// int32, not uint and not 64bit because: javascript uses 32int
56
46
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND
@@ -69,13 +59,63 @@ class BitArray
69
59
this . wordArray [ wordOffset ] |= ( 1 << bitOffset ) ;
70
60
}
71
61
62
+ setBitsTrue ( range_start , step , range_stop ) {
63
+ if ( step > WORD_SIZE / 2 ) {
64
+ // steps are large: check if the range is large enough to reuse the same mask
65
+ let range_stop_unique = range_start + 32 * step ;
66
+ if ( range_stop_unique > range_stop ) {
67
+ // range is not large enough for repetition (32 * step)
68
+ for ( let index = range_start ; index < range_stop ; index += step ) {
69
+ this . setBitTrue ( index ) ;
70
+ }
71
+ return ;
72
+ }
73
+ // range is large enough to reuse the mask
74
+ const range_stop_word = range_stop >>> 5 ;
75
+ for ( let index = range_start ; index < range_stop_unique ; index += step ) {
76
+ let wordOffset = index >>> 5 ;
77
+ const bitOffset = index & 31 ;
78
+ const mask = ( 1 << bitOffset ) ;
79
+ do {
80
+ this . wordArray [ wordOffset ] |= mask ;
81
+ wordOffset += step ; // pattern repeats on word level after {step} words
82
+ } while ( wordOffset <= range_stop_word ) ;
83
+ }
84
+ return ;
85
+ }
86
+
87
+ // optimized for small sizes: set wordvalue multiple times before committing to memory
88
+ let index = range_start ;
89
+ let wordOffset = index >>> 5 ; // 1 word = 2ˆ5 = 32 bit, so shift 5, much faster than /32
90
+ let wordValue = this . wordArray [ wordOffset ] ;
91
+
92
+ while ( index < range_stop ) {
93
+ const bitOffset = index & 31 ; // use & (and) for remainder, faster than modulus of /32
94
+ wordValue |= ( 1 << bitOffset ) ;
95
+
96
+ index += step ;
97
+ const newwordOffset = index >>> 5 ; // 1 word = 2ˆ5 = 32 bit, so shift 5, much faster than /32
98
+ if ( newwordOffset != wordOffset ) { // moving to new word: store value and get new value
99
+ this . wordArray [ wordOffset ] = wordValue ;
100
+ wordOffset = newwordOffset ;
101
+ wordValue = this . wordArray [ wordOffset ] ;
102
+ }
103
+ }
104
+ this . wordArray [ wordOffset ] = wordValue ; // make sure last value is stored
105
+ }
106
+
72
107
testBitTrue ( index )
73
108
{
74
109
const wordOffset = index >>> 5 ;
75
110
const bitOffset = index & 31 ;
76
111
return this . wordArray [ wordOffset ] & ( 1 << bitOffset ) ; // use a mask to only get the bit at position bitOffset. >0=true, 0=false
77
112
}
78
113
114
+ searchBitFalse ( index )
115
+ {
116
+ while ( this . testBitTrue ( index ) ) { index ++ } ; // will stop automatically because bits were 0 filled
117
+ return index ;
118
+ }
79
119
}
80
120
81
121
/*
@@ -89,35 +129,32 @@ class PrimeSieve
89
129
constructor ( sieveSize )
90
130
{
91
131
this . sieveSize = sieveSize ;
92
- this . oddsize = sieveSize >>> 1 ;
93
- this . bitarray = new BitArray ( 1 + this . oddsize ) ;
132
+ this . sieveSizeInBits = sieveSize >>> 1 ;
133
+ this . bitArray = new BitArray ( 1 + this . sieveSizeInBits ) ;
94
134
}
95
135
96
136
runSieve ( )
97
137
{
98
- const q = Math . ceil ( Math . sqrt ( this . oddsize ) ) ; // convert to integer with ceil
138
+ const q = Math . ceil ( Math . sqrt ( this . sieveSizeInBits ) ) ; // convert to integer with ceil
139
+ let factor = 1 ;
99
140
100
- for ( let factor = 1 ; factor <= q ; factor ++ )
141
+ while ( factor < q )
101
142
{
102
- if ( ! this . bitarray . testBitTrue ( factor ) )
103
- {
104
- const step = factor * 2 + 1 ;
105
- const start = factor * factor * 2 + factor + factor ;
143
+ const step = factor * 2 + 1 ;
144
+ const start = factor * factor * 2 + factor + factor ;
106
145
107
- for ( let multiple = start ; multiple < this . oddsize ; multiple = multiple + step )
108
- {
109
- this . bitarray . setBitTrue ( multiple ) ; // mark every multiple of this prime
110
- }
111
- }
146
+ this . bitArray . setBitsTrue ( start , step , this . sieveSizeInBits ) ; // mark every multiple of this prime
147
+ factor = this . bitArray . searchBitFalse ( factor + 1 ) ;
112
148
}
149
+ return this ;
113
150
}
114
151
115
152
countPrimes ( )
116
153
{
117
154
let total = 1 ; // account for prime 2
118
- for ( let index = 1 ; index < this . oddsize ; index ++ )
155
+ for ( let index = 1 ; index < this . sieveSizeInBits ; index ++ )
119
156
{
120
- if ( ! this . bitarray . testBitTrue ( index ) ) // if bit is false, it's a prime, because non-primes are marked true
157
+ if ( ! this . bitArray . testBitTrue ( index ) ) // if bit is false, it's a prime, because non-primes are marked true
121
158
{
122
159
total ++ ;
123
160
}
@@ -128,14 +165,54 @@ class PrimeSieve
128
165
getPrimes ( max = 100 )
129
166
{
130
167
const primes = [ 2 ] ; // 2 is a special prime
131
- for ( let factor = 1 , count = 0 ; factor < this . oddsize ; factor ++ )
168
+ for ( let factor = 1 , count = 0 ; factor < this . sieveSizeInBits ; factor ++ )
132
169
{
133
170
if ( count >= max ) break ;
134
- if ( ! this . bitarray . testBitTrue ( factor ) ) count = primes . push ( factor * 2 + 1 ) ;
171
+ if ( ! this . bitArray . testBitTrue ( factor ) ) count = primes . push ( factor * 2 + 1 ) ;
135
172
}
136
173
return primes ;
137
174
}
138
175
176
+ validatePrimeCount ( verbose )
177
+ {
178
+ // Historical data for validating our results - the number of primes
179
+ // to be found under some limit, such as 168 primes under 1000
180
+ const maxShowPrimes = 100 ;
181
+ const knownPrimeCounts = {
182
+ 10 : 4 ,
183
+ 100 : 25 ,
184
+ 1000 : 168 ,
185
+ 10000 : 1229 ,
186
+ 100000 : 9592 ,
187
+ 1000000 : 78498 ,
188
+ 10000000 : 664579 ,
189
+ 100000000 : 5761455
190
+ } ;
191
+ const countedPrimes = this . countPrimes ( ) ;
192
+ const primeArray = this . getPrimes ( maxShowPrimes ) ;
193
+
194
+ let validResult = false ;
195
+ if ( this . sieveSize in knownPrimeCounts )
196
+ {
197
+ const knownPrimeCount = knownPrimeCounts [ this . sieveSize ] ;
198
+ validResult = ( knownPrimeCount == countedPrimes ) ;
199
+ if ( ! validResult )
200
+ console . log (
201
+ "\nError: invalid result." ,
202
+ `Limit for ${ this . sieveSize } should be ${ knownPrimeCount } ` ,
203
+ `but result contains ${ countedPrimes } primes`
204
+ ) ;
205
+ }
206
+ else console . log (
207
+ `Warning: cannot validate result of ${ countedPrimes } primes:` ,
208
+ `limit ${ this . sieveSize } is not in the known list of number of primes!`
209
+ ) ;
210
+
211
+ if ( verbose )
212
+ console . log ( `\nThe first ${ maxShowPrimes } found primes are:` , primeArray ) ;
213
+
214
+ return validResult ;
215
+ }
139
216
}
140
217
141
218
// run the sieve for timeLimitSeconds
@@ -157,80 +234,19 @@ const runSieveBatch = (sieveSize, timeLimitSeconds = 5) =>
157
234
return nrOfPasses ;
158
235
}
159
236
160
- // get a single sieve (for validation and statistics)
161
- const evalSieve = ( sieveSize , maxShowPrimes = 100 ) =>
237
+ // main procedure
238
+ const main = ( { sieveSize, timeLimitSeconds , verbose , runtime } ) =>
162
239
{
163
- const sieve = new PrimeSieve ( sieveSize ) ;
164
- sieve . runSieve ( ) ;
165
- return {
166
- countedPrimes : sieve . countPrimes ( ) ,
167
- primeArray : sieve . getPrimes ( maxShowPrimes )
168
- }
169
- }
240
+ // validate algorithm - run one time
241
+ const validResult = new PrimeSieve ( sieveSize ) . runSieve ( ) . validatePrimeCount ( verbose ) ;
242
+ if ( ! validResult ) return false ;
170
243
171
- /*
172
- main procedure
173
- */
174
-
175
- const main = ( { sieveSize, timeLimitSeconds, verbose, maxShowPrimes } ) =>
176
- {
177
- // run once, without threads
178
244
//measure time running the batch
179
245
const timeStart = performance . now ( ) ;
180
246
const totalPasses = runSieveBatch ( sieveSize , timeLimitSeconds ) ;
181
247
const timeEnd = performance . now ( ) ;
182
248
const durationInSec = ( timeEnd - timeStart ) / NOW_UNITS_PER_SECOND ;
183
-
184
- // validate algorithm - run one final time on the result
185
- const sieveResult = evalSieve ( sieveSize ) ;
186
- let validResult = false ;
187
- if ( sieveSize in knownPrimeCounts )
188
- {
189
- const knownPrimeCount = knownPrimeCounts [ sieveSize ] ;
190
- validResult = ( knownPrimeCount == sieveResult . countedPrimes ) ;
191
- if ( ! validResult )
192
- console . log (
193
- "\nError: invalid result." ,
194
- `Limit for ${ sieveSize } should be ${ knownPrimeCount } ` ,
195
- `but result contains ${ sieveResult . countedPrimes } primes`
196
- ) ;
197
- }
198
- else console . log (
199
- `Warning: cannot validate result of ${ sieveResult . countedPrimes } primes:` ,
200
- `limit ${ sieveSize } is not in the known list of number of primes!`
201
- ) ;
202
-
203
- if ( validResult )
204
- {
205
- const res = [
206
- `\nrogiervandam-${ runtime } ` ,
207
- totalPasses . toString ( ) ,
208
- durationInSec . toString ( ) ,
209
- "1" ,
210
- "algorithm=base,faithful=yes,bits=1"
211
- ] ;
212
- console . log ( res . join ( ";" ) ) ;
213
- }
214
-
215
- if ( verbose )
216
- {
217
- console . log ( `\nThe first ${ maxShowPrimes } found primes are:` , sieveResult . primeArray ) ;
218
- console . log (
219
- `Passes: ${ totalPasses } ,` ,
220
- `Time: ${ ( durationInSec ) . toFixed ( 2 ) } ,` ,
221
- `Avg: ${ ( durationInSec / totalPasses ) . toFixed ( 8 ) } (sec/pass),` ,
222
- `Sieve size: ${ sieveSize } ,` ,
223
- `Primes: ${ sieveResult . countedPrimes } ,` ,
224
- `Valid: ${ validResult } `
225
- ) ;
226
- }
249
+ console . log ( `\nrogiervandam-${ runtime } ;${ totalPasses } ;${ durationInSec } ;1;algorithm=base,faithful=yes,bits=1` ) ;
227
250
}
228
251
229
- const config = {
230
- sieveSize : 1000000 ,
231
- timeLimitSeconds : 5 ,
232
- verbose : verbose ,
233
- maxShowPrimes : verbose ? 100 : 0
234
- } ;
235
-
236
252
main ( config ) ;
0 commit comments