You HAVE to do benchmarks

Assume that we have a piece of code in which we have to compute the reciprocal of each element of a big vector, in Clojure we can use the map function

(def v (vec (range 1 1000000)))
(def result (doall (map #(/ 1.0 %) v)))

The doall just guarantee that all the values get calculated, because map returns a lazy sequence, but in the following, it will be necessary to get the actual values.

Later on, we discover that a parallel version of map exists and so we may decide to replace the previous code with

(def v (vec (range 1 1000000)))
(def result (doall (pmap #(/ 1.0 %) v)))

Multiple threads make our code more complex, the only reason why one may decide to use them is for performance. Every change must carefully compare the advantages with the complexity.

The easiest way to get an idea of the performance is the time function, we can compare the previous implementation using

(def v (vec (range 1 1000000)))
(def result0 (time (doall (map #(/ 1.0 %) v))))
(def result1 (time (doall (pmap #(/ 1.0 %) v))))

On my PC I get 165.576588 msecs to calculate result0 and 1965.176543 msecs to calculate result1. The code that uses pmap is more than an order of magnitude worst! The reason is stated in the documentation

Only useful for computationally intensive functions where the time of f dominates the coordination overhead

Is time enough?

Let's try to run time multiple times

(def v (vec (range 1 1000000)))
(def result0 (time (doall (map #(/ 1.0 %) v))))
(def result1 (time (doall (map #(/ 1.0 %) v))))

On my machine, the second time took much less. When one do some benchmark on the JVM, one has to run the code multiple time, this is known as a "warm-up".

There are specialized libraries for benchmarking that take care of warm-up and measuring. The library we will discuss here is a criterium.

We just have to add the dependency [criterium "0.4.6"] (at the time of writing).

And then we can benchmark using

(require '[criterium.core :as criterium])
(def v (vec (range 1 1000000)))
(def result (criterium/quick-bench (doall (map #(/ 1.0 %) v))))