I’ve been testing my Segmented Prime Sieves code with 1.19 to great success.
It seems all the old problems w/fibers are gone, and now the code runs reliably.
The one significant remaining issue is the number of fibers that can run concurrently.
Because Crystal doesn’t have true multi-thread parallelism yet, I’m limited to the size
of numbers I can process the code creates more spawned threads for.
In Rust, D, etc, they just recycle the number of available system primes to a spawn
process until all are done. But in Crystal I have to pre-allocate their number and wait
for them to finish, as shown in this code.
cnts = Array(UInt64).new(pairscnt, 0)
lastwins = Array(UInt64).new(pairscnt, 0)
wg = WaitGroup.new(pairscnt)
threadscnt = Atomic.new(0)
restwins.each_with_index do |r_hi, i|
spawn do
lastwins[i], cnts[i] = twins_sieve(r_hi, kmin, kmax, ks, start_num, end_num, modpg, primes, resinvrs)
print "\r#{threadscnt.add(1)} of #{pairscnt} twinpairs done"
wg.done
end end
wg.wait
However, once pairscnt reaches a certain size this error is encountered:
Unhandled exception: Cannot allocate new fiber stack: Cannot allocate memory (RuntimeError)
from ./primes_ssoz_newref in '??'
from ./primes_ssoz_newref in '??'
from ./primes_ssoz_newref in '??'
from ./primes_ssoz_newref in '??'
from ./primes_ssoz_newref in '??'
from ./primes_ssoz_newref in 'main'
from /lib64/libc.so.6 in '??'
from /lib64/libc.so.6 in '__libc_start_main'
from ./primes_ssoz_newref in '_start'
from ???
My question: Is there a way to do fiber pool sharing to get around this?
Can I create a finite number of fibers that get reused as needed when used ones finish?
The Crystal code runs now much closer to Rust, D, etc times.
But they have consistent low memory use too while Crystal’s grows w/fiber use.
Thus after a point, Crystal can’t process the large inputs values they can.
But again, I’m very pleased with 1.19s fibers improvement.
Can’t wait for true multi-thread parallelism to be on par with the others.