Clojure's lazy sequence versus transducer benchmark
(Posted on 2022-04-20)
According to this thread, I compared using only lazy sequences with transducers.
To add the I/O factor, I prepared a data file called “fake.txt” using the program below:
(with-open [w (io/writer "fake.txt")]
(doseq [n (range 10000000)]
(.write w (str n "\n"))))
F1 is the lazy-sequence-based version. It reads data from “fake.txt” and does a few steps of computations.
(defn f1
[]
(with-open [r (io/reader "fake.txt")]
(->> (line-seq r)
(map parse-long)
(map inc)
(filter even?)
(map inc)
(reduce + 0))))
F2 is the transducer-based version of F1.
(defn f2
[]
(with-open [r (io/reader "fake.txt")]
(transduce (comp (map parse-long)
(map inc)
(filter even?)
(map inc))
+
(line-seq r))))
I evaluated them using Criterium.
(with-progress-reporting (quick-bench (f1) :verbose))
(with-progress-reporting (quick-bench (f2) :verbose))
Here is the result.
#################### F1 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 3.811858 sec
Execution time mean : 3.812064 sec
#################### F2 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 1.490624 sec
Execution time mean : 1.490777 sec
F1, which is the lazy sequence version, took 3.812064 seconds. F2, which is the transducer version, took 1.490777. So the transducer version is 155.71% faster than the lazy sequence version.
In brief, this biased experiment shows the transducer version is much faster than the pure lazy sequence version.