Here is a short Scala program which computes a poor approximation of Euler's number by generating a bunch of random numbers:
package example
import scala.util.Random
object ApproxE extends App {
def calc = {
var S = 0.0
for {i <- 1 to 100000000} {
var x = 0.0
var t = 0
while (x <= 1.0) {
x += Random.nextDouble
t += 1
}
S += t
}
S / 100000000
}
val st = System.currentTimeMillis()
val e = calc
val ed = System.currentTimeMillis()
println(e)
println(ed - st)
}
Running on my laptop, it does its computation in about 7 seconds.
The following are two equivalent Clojure functions. On returns in 48 seconds, and the other in 230 seconds.
Is it possible, in pure Clojure, to write an equivalent program with performance comparable to that achievable in a Java or Scala?
48s:
(defn calc []
(loop [i (int 0)
S (double 0.0)]
(if (= i 100000000)
(/ S 100000000)
(let [rs (repeatedly rand)
ps (reductions + rs)
<=1 #(<= % 1)]
(->> ps
(take-while <=1)
(count)
(inc)
(+ S)
(recur (inc i)))))))
230s:
(defn calc2 []
(with-local-vars [S 0.0]
(dotimes [i 100000000]
(with-local-vars [x (double 0)
t (int 0)]
(while (<= @x 1)
(var-set x (+ @x (rand)))
(var-set t (inc @t)))
(var-set S (+ @S @t))))
(/ @S 100000000)))
Both lazy data structures and local vars introduce an overhead due to allocations (though you can still make the variant with the vars a bit faster by moving the allocations for x
and t
out of the loop), name resolution (in case of vars) and method calling.
A word-to-word translation of the Scala code would use local bindings (using let
and loop
), which are equivalent to Java's local variables:
(defn calc-loops []
(loop [i (int 0)
S (double 0.0)]
(if (= i 100000000)
(/ S 100000000)
(recur
(inc i)
(loop [t 0 x 0.0]
(if (<= x 1.0)
(recur (inc t) (+ x (rand)))
(+ S t)))))))
(time (calc)) ;; => "Elapsed time: 56255.692677 msecs"
(time (calc-loops)) ;; => "Elapsed time: 8800.746127 msecs"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With