-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This library contains 3 generators to choose from, based on your use case. Also check the benchmark performance figures.
-
Default
primeGenenerator()
- produces primes from 2 untilmaxPrime = 9_007_199_254_740_881
, which is hypothetically reachable, after running for 10+ years, so it can be considered infinite. This is the most efficient method in terms of memory and CPU usage, and can produce the first million primes in under half-second. -
Fast
primeGenerator({boost: N})
- produces up toN
primes. It boosts performance 10x times over the default method, by pre-allocating a memory buffer for the calculation. However, this brings memory penalty, and soN
is capped at 100mln primes, which at peak will use about 127MB of RAM. -
Offset
primeGenerator({start: S})
- produces all primes betweenS
(inclusive) andmaxPrime
. It has the same memory + CPU efficiency as the default method, and is the best at finding primes above a certain range.
Note that you cannot combine start
and boost
options, because the fast method can only produce primes from the beginning, while the offset method cannot buffer its calculation.
If you need to store a large number of primes in memory, for quick access, and concerned about memory usage, this library has a function cachePrimes to help with that. It caches up primes as compressed gaps, up to 100mln primes (100MB), and gives quick access to all of them as iterable or array. You end up using 8 times less memory than by keeping actual primes.
import {cachePrimes} from 'prime-lib';
const cache = cachePrimes(1e7); // internally uses generatePrimes({boost: 1e7})
for(const prime of cache) {
// good performance + memory usage;
// ~780ms to iterate through 100mln primes
// ~70ms to iterate through 10mln primes
}
const {fastIndex} = cache;
for(let i = 0;i < fastIndex.length;i ++) {
// excellent performance + memory usage;
// ~325ms to iterate through 100mln primes
// ~32ms to iterate through 10mln primes
const prime = fastIndex.get(i);
}
for(let i = 0;i < cache.length;i ++) {
// good memory usage, but the worst possible performance,
// because each [i] call goes through a very slow proxy;
// ~30s to iterate through 100mln primes
// ~2.3s to iterate through 10mln primes
const prime = cache[i];
}
// great performance, but terrible memory usage;
// can't handle 100mln primes (overloads the memory)
const list = [...cache]; //=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
// best performance, but terrible memory usage;
// can barely handle 100mln primes (overloads the memory)
const primes = Array.from(cache);
The returned cache object is RXJS-friendly:
import {from, Observable} from 'rxjs';
import {cachePrimes} from 'prime-lib';
const cache = cachePrimes(10);
from(cache).subscribe(prime => {
// 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
// ~42s for 100mln primes and ~3.5s for 10mln primes
// yeah, RXJS itself is slow (tested with RXJS v7.4.0)
});
// it works much faster with a manual observable from index:
const primes = new Observable(obs => {
const {get, length} = cache.fastIndex;
for (let i = 0; i < length; i++) {
obs.next(get(i));
}
obs.complete();
});
primes.subscribe(prime => {
// 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
// ~7s for 100mln primes and ~0.7s for 10mln primes
// much better, but still 20 times slower than direct index access
});
See also - I opened a performance-related issue against RXJS.
This library includes 3 stop-iterators - stopOnValue
, stopOnCount
, and generic stopWhen
, to help limit generators. These are mainly for regular JavaScript and TypeScript clients, as RXJS provides its own operators for this.
However, you can combine them with RXJS, if you like - see the example below.
Let's produce all prime numbers between 100 and 200:
import {from} from 'rxjs';
import {generatePrimes, stopOnValue} from 'prime-lib';
const iterator = stopOnValue(generatePrimes({start: 100}), 200);
from(iterator)
.subscribe(prime => {
// 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
// 157, 163, 167, 173, 179, 181, 191, 193, 197, 199
});
The above result is the same as the following:
import {from, takeWhile} from 'rxjs';
import {generatePrimes} from 'prime-lib';
const iterator = generatePrimes({start: 100});
from(iterator).pipe(takeWhile(p => p < 200))
.subscribe(prime => {
// 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
// 157, 163, 167, 173, 179, 181, 191, 193, 197, 199
});