Ok, I’m finally getting somewhere. To start with, Rich pointed out that I could use recur to make my last prime finder stack safe and faster. Here it is:
(defn primes-in-list [potential-primes known-primes]
(let [p (first potential-primes)]
(if (nil? p) ‘()
(if (is-prime? p known-primes)
(lazy-cat
(list p)
(primes-in-list (rest potential-primes) (conj known-primes p)))
(let [rest-primes (rest potential-primes)]
(recur rest-primes known-primes))))))
Here’s how I parallelized it:
(def w1 (agent 0))
(def w2 (agent 0))
(defn par-primes-in-list [potential-primes known-primes]
(let [ x 10 x-times-2 (* x 2)]
(! w1 (fn[w pp kp] (primes-in-list pp kp)) (take x potential-primes) known-primes)
(! w2 (fn[w pp kp] (primes-in-list pp kp)) (take x (drop x potential-primes)) known-primes)
(await w1 w2)
(let [new-primes (concat @w1 @w2) ]
(if (= (count new-primes) 0)
(recur (drop x-times-2 potential-primes) known-primes)
(let [real-primes
(if (> (first new-primes) x-times-2) new-primes (primes-in-list new-primes []))]
(lazy-cat real-primes (par-primes-in-list
(drop x-times-2 potential-primes)
(into known-primes real-primes))))))))
Basically, I send lists to the agents, wait for them, and then combine their answers. The original runs at about 100% of CPU, calculates 8000 primes in 21.6 seconds and this runs at about 125%, and calculates the primes in 17.1 seconds, or in about 80% of the time.