Timing expressions

5 views
Skip to first unread message

Frantisek Sodomka

unread,
Aug 3, 2008, 5:25:53 PM8/3/08
to clo...@googlegroups.com
Hello all!

Clojure already has function 'time' for timing expressions. To make
comparing of time difference of different expressions easier, here is a
function 'time-expr':

(clojure/refer 'clojure)

(defn format
"Returns a string using the specified format and arguments. See
java.util.Formatter for format string syntax."
[fmt & args]
(.format String fmt (to-array args)))

(defmacro time-ms [n expr]
`(let [start# (. System (nanoTime))]
(dotimes i# ~n ~expr)
(/ (double (- (. System (nanoTime)) start#)) 1000000.0)))

(defmacro time-expr [n & expr]
;; check equality of expressions
(let [eval-expr# (map eval expr)]
(when-not (eval (apply and (map = (butlast eval-expr#) (rest
eval-expr#))))
(prn "Expressions: ")
(dorun (map prn expr))
(throw (new Exception "Expression results do not match!"))))

;; time expressions and print results
(let [times# (eval (cons 'list (map (fn [e] (list 'time-ms n e)) expr)))
max-time# (apply max times#)
percent# (map (fn [t] (* 100.0 (/ t max-time#))) times#)
ratio# (map (fn [t] (/ max-time# t)) times#)]
(dorun (map
(fn [t p r e] (print (format "%8.2f ms %6.1f%% %5.1fx " t p r)) (prn
e))
times# percent# ratio# expr))
(newline)))


And some tests:

;; simple test ;;

(time-expr 10000 (+ 1 2) 3 (/ (* 3 3) 1 3))


;; test len ;;

(defn len [x]
(. x (length)))

(defn len2 [#^String x]
(. x (length)))

(defn len3 [#^String x]
(.length x))

(time-expr 5
(reduce + (map len (replicate 1000 "abc")))
(reduce + (map len2 (replicate 1000 "abc")))
(reduce + (map len3 (replicate 1000 "abc"))) )

(def s "abcdef")
(time-expr 1000
(len s)
(len2 s)
(len3 s))


;; test loop/recur with type hints ;;

(defn foo [n]
(loop [i 0]
(if (< i n)
(recur (inc i))
i)))

(defn foo2 [n]
(let [n (int n)]
(loop [i (int 0)]
(if (< i n)
(recur (inc i))
i))))

(time-expr 3
(foo 100000)
(foo2 100000))


Which on my computer prints something like:

2.50 ms 11.4% 8.8x (+ 1 2)
1.27 ms 5.8% 17.2x 3
21.87 ms 100.0% 1.0x (/ (* 3 3) 1 3)

84.93 ms 100.0% 1.0x (reduce + (map len (replicate 1000 "abc")))
8.90 ms 10.5% 9.5x (reduce + (map len2 (replicate 1000 "abc")))
9.08 ms 10.7% 9.3x (reduce + (map len3 (replicate 1000 "abc")))

15.95 ms 100.0% 1.0x (len s)
0.36 ms 2.2% 44.8x (len2 s)
0.35 ms 2.2% 45.8x (len3 s)

46.86 ms 100.0% 1.0x (foo 100000)
2.64 ms 5.6% 17.8x (foo2 100000)

The first column is time, the second are percents (the longest time vs.
others), the third are ratios of speed (the more the better).

This is my first bigger piece of Clojure code. Suggestions welcome!

Frantisek

PS: Written along the lines of
http://newlisp-on-noodles.org/wiki/index.php/Timing

Frantisek Sodomka

unread,
Aug 6, 2008, 5:03:39 PM8/6/08
to clo...@googlegroups.com
Updated version of 'time-expr':

(defmacro time-expr [n & expr]
;; check equality of expressions

(when-not (apply = (map eval expr))


(prn "Expressions: ")
(dorun (map prn expr))
(throw (new Exception "Expression results do not match!")))

;; time expressions and print results


(let [times# (eval (cons 'list (map (fn [e] (list 'time-ms n e)) expr)))
max-time# (apply max times#)
percent# (map (fn [t] (* 100.0 (/ t max-time#))) times#)
ratio# (map (fn [t] (/ max-time# t)) times#)]
(dorun (map
(fn [t p r e] (print (format "%8.2f ms %6.1f%% %5.1fx " t p r)) (prn
e))
times# percent# ratio# expr))
(newline)))

Frantisek

Frantisek Sodomka

unread,
Aug 9, 2008, 9:45:31 AM8/9/08
to clo...@googlegroups.com
;; fibonacci ;;

(defn fib-simple [n]
(cond
(> n 1)
(+ (fib-simple (- n 1)) (fib-simple (- n 2)))
(= n 1)
1
(= n 0)
0))

(defn fib-optimized [n]
(if (zero? n) 0
(loop [a 0 b 1 n n]
(if (= n 1) b
(recur b (+ a b) (dec n))))))

(defn fib-lazy
([] (concat [0 1] (fib-lazy 0 1)))
([a b] (lazy-cons (+ a b) (fib-lazy b (+ a b)))))

(def fib-seq
(concat
[0 1]
((fn rfib [a b]
(lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))

(def N 20)

(time-expr 25
(dorun (map fib-simple (range N)))
(dorun (map fib-optimized (range N)))
(dorun (take N (fib-lazy)))
(dorun (take N fib-seq)))


Prints:

219.34 ms 100.0% 1.0x (dorun (map fib-simple (range N)))
2.35 ms 1.1% 93.3x (dorun (map fib-optimized (range N)))
1.59 ms 0.7% 137.9x (dorun (take N (fib-lazy)))
0.38 ms 0.2% 579.4x (dorun (take N fib-seq))


Anyway - is my use of 'dorun' correct? Shouldn't I use 'doall'?

Greetings, Frantisek

Reply all
Reply to author
Forward
0 new messages