bad performance when passing around boolean-arrays in clojure -
i've started solving project euler problems in clojure means of learning language. when implementing sieve of eratosthenes problem 10 experienced extremely poor performance appears stem passing around boolean-array
. i've found can work around problem either adding type hints or reordering code helper functions can access boolean-array
directly parent scope, don't understand why it's needed. (type sieve)
in of helper functions returns [z
, appears clojure knows it's boolean-array
. can please explain why type hints needed here?
apologies wall of code, don't know parts can removed while still illustrating problem.
;;; returns vector containing primes smaller limit (defn gen-primes-orig [limit] (defn next-unmarked [sieve v] (loop [i (inc v)] (cond (>= limit) nil (false? (aget sieve i)) :true (recur (inc i))))) (defn mark-powers-of! [sieve v] (loop [i (+ v v)] (if (>= limit) sieve (do (aset-boolean sieve true) (recur (+ v)))))) (defn collect-primes [sieve] (loop [primes [] 0] (cond (>= limit) primes (false? (aget sieve i)) (recur (conj primes i) (inc i)) :true (recur primes (inc i))))) (let [sieve (boolean-array limit false)] ;; 0 , 1 not primes (aset-boolean sieve 0 true) (aset-boolean sieve 1 true) (loop [v 0] (let [v (next-unmarked sieve v)] (if (nil? v) (collect-primes sieve) (do (mark-powers-of! sieve v) (recur v))))))) (defn gen-primes-hint [limit] (defn next-unmarked [^booleans sieve v] ;; same body in gen-primes-orig ) (defn mark-powers-of! [^booleans sieve v] ;; same body in gen-primes-orig ) (defn collect-primes [^booleans sieve] ;; same body in gen-primes-orig ) (let [sieve (boolean-array limit false)] ;; same body in gen-primes-orig )) (defn gen-primes-letfn [limit] (let [sieve (boolean-array limit false)] ;; 0 , 1 not primes (aset-boolean sieve 0 true) (aset-boolean sieve 1 true) (letfn [(next-unmarked [v] ;; same body in gen-primes-orig ) (mark-powers-of! [v] ;; same body in gen-primes-orig ) (collect-primes [] ;; same body in gen-primes-orig )] (loop [v 0] (let [v (next-unmarked v)] (if (nil? v) (collect-primes) (do (mark-powers-of! v) (recur v))))))))
and here's running time each of 3 blends. (result has been removed. blends produce same, correct result.)
user> (time (apply + (gen-primes-orig 2000000))) "elapsed time: 108001.047327 msecs" user> (time (apply + (gen-primes-hint 2000000))) "elapsed time: 2599.091978 msecs" user> (time (apply + (gen-primes-letfn 2000000))) "elapsed time: 2768.060355 msecs"
type
function uses .getclass
method on passed object return class of object. i.e uses runtime reflection figure out class of object. type hints acts clojure compiler generate efficient code avoiding generation of reflection based code. type
has nothing type hints helps clojure compiler emit efficient code.
Comments
Post a Comment