I was doing some macro work and was curious about the cost of an inline `eval` vs compiled code. I tried to time it with `clojure.core/time` and got results I can't explain:
(println :eval-time)
(time
(do
(println (eval (quote (+ 40 0))))
(println (eval (quote (+ 40 1))))
(println (eval (quote (+ 40 2))))
(println (eval (quote (+ 40 3))))
(println (eval (quote (+ 40 4))))))
:eval-time
40
41
42
43
44
"Elapsed time: 0.922536 msecs"
On a modern computer, this is an insanely long amount of time. And yes, it is approx 5x longer than doing just one line of the above. I also tried it without the eval:
(println :add-time)
(time
(do
(println (+ 30 0))
(println (+ 30 1))
(println (+ 30 2))
(println (+ 30 3))
(println (+ 30 4))))
:add-time
30
31
32
33
34
"Elapsed time: 0.01344 msecs"
Still seems pretty slow to just print 5 lines with a single addition each.
The `time` macro is very simple, and is often used as introductory example of how macros work and when they are needed:
(defmacro time
"Evaluates expr and prints the time it took. Returns the value of
expr."
{:added "1.0"}
[expr]
`(let [start# (. System (nanoTime))
ret# ~expr]
(prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
ret#))
I coincidentally had a homegrown timer available with nearly identical code. It has results:
(prof/with-timer-print :add-prof
(do
(println (+ 10 0))
(println (+ 10 1))
(println (+ 10 2))
(println (+ 10 3))
(println (+ 10 4))))
10
11
12
13
14
:with-timer-print :add-prof 0.000048
and
(prof/with-timer-print :eval-prof
(do
(println (eval (quote (+ 20 0))))
(println (eval (quote (+ 20 1))))
(println (eval (quote (+ 20 2))))
(println (eval (quote (+ 20 3))))
(println (eval (quote (+ 20 4))))))
20
21
22
23
24
:with-timer-print :eval-prof 0.001041
So we see the times are much, much shorter. The timing code is nearly identical to clojure.core/time:
(defmacro with-timer-result
"Times execution of Clojure forms, returning a result map like:
{:result result :seconds seconds} "
[& forms]
`(let [start# (System/nanoTime)
result# (do ~@forms)
stop# (System/nanoTime)
elapsed# (double (- stop# start#))
seconds# (/ elapsed# 1e9)]
{:result result#
:seconds seconds#}))
(defmacro with-timer-print
"Times execution of Clojure forms, printing the result to the screen. "
[id & forms]
(when-not (keyword? id)
(throw (ex-info "id must be a keyword" (vals->map id))))
`(let [result-map# (with-timer-result ~@forms)]
(println (format ":with-timer-print %s %12.6f" ~id (grab :seconds result-map#)))
(grab :result result-map#)))
Does anyone have an idea of why `clojure.core/time` gives such insanely inflated results?
Alan
PS. I ran the above 4 sequences multiple times using lein-test-refresh, and these were the shortest I could get. I'm pretty confident the answer is not loading, compiling, or JIT related.