Spoiler alert, this is problem 5 of Project Euler.
I am attempting to learn Clojure and solved problem 5, but it is a couple orders of magnitude slower (1515 ms in Java versus 169932 ms in Clojure). I even tried using type hinting, unchecked math operations, and inlining functions all for naught.
Why is my Clojure code so much slower?
Clojure code:
(set! *unchecked-math* true)
(defn divides? [^long number ^long divisor] (zero? (mod number divisor)))
(defn has-all-divisors [divisors ^long num]
(if (every? (fn [i] (divides? num i)) divisors) num false))
(time (prn (some (fn [^long i] (has-all-divisors (range 2 20) i)) (iterate inc 1))))
Java code:
public class Problem5 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
int i = 1;
while(!hasAllDivisors(i, 2, 20)) {
i++;
}
long end = System.currentTimeMillis();
System.out.println(i);
System.out.println("Elapsed time " + (end - start));
}
public static boolean hasAllDivisors(int num, int startDivisor, int stopDivisor) {
for(int divisor=startDivisor; divisor<=stopDivisor; divisor++) {
if(!divides(num, divisor)) return false;
}
return true;
}
public static boolean divides(int num, int divisor) {
return num % divisor == 0;
}
}
Some performance problems:
(range 2 20)call is creating a new lazy list of numbers for every increment ofi. This is expensive, and is causing lots of unnecessary GC.(iterate inc 1)is doing boxing / unboxing at every increment.modis actually not a very well optimised function in Clojure at present. You are much better off usingremYou can solve the first problem by using a
letstatement to define the range just once:You can solve the second problem with loop/recur:
You can solve the third problem by using an iterative loop over the possible divisors:
You can solve the final problem using
remAs you can see, it is now competitive with the Java version.
In general, you can usually make Clojure almost as fast as Java with a bit of effort. The main tricks are usually:
(set! *warn-on-reflection* true)and eliminate all warnings you find)