2016-06-05 15:39:58 +00:00
|
|
|
(ns com.rpl.specter.benchmarks
|
|
|
|
|
(:use [com.rpl.specter]
|
|
|
|
|
[com.rpl.specter macros]
|
|
|
|
|
[com.rpl.specter.impl :only [benchmark]])
|
|
|
|
|
(:require [clojure.walk :as walk]))
|
|
|
|
|
|
|
|
|
|
;; run via `lein repl` with `(load-file "scripts/benchmarks.clj")`
|
|
|
|
|
|
|
|
|
|
(defn pretty-float5 [anum]
|
|
|
|
|
(format "%.5g" anum))
|
|
|
|
|
|
|
|
|
|
(defn pretty-float3 [anum]
|
|
|
|
|
(format "%.3g" anum))
|
|
|
|
|
|
|
|
|
|
(defn time-ms [amt afn]
|
|
|
|
|
(let [start (System/nanoTime)
|
|
|
|
|
_ (dotimes [_ amt] (afn))
|
|
|
|
|
end (System/nanoTime)]
|
|
|
|
|
(/ (- end start) 1000000.0)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
(defn avg [numbers]
|
|
|
|
|
(/ (reduce + numbers)
|
|
|
|
|
(count numbers)
|
|
|
|
|
1.0))
|
|
|
|
|
|
|
|
|
|
(defn average-time-ms [iters amt-per-iter afn]
|
|
|
|
|
(avg
|
2016-06-07 00:29:45 +00:00
|
|
|
;; treat 1st run as warmup
|
|
|
|
|
(next
|
|
|
|
|
(for [i (range (inc iters))]
|
|
|
|
|
(time-ms amt-per-iter afn)))))
|
2016-06-05 15:39:58 +00:00
|
|
|
|
2016-06-05 19:25:00 +00:00
|
|
|
(defn compare-benchmark [amt-per-iter afn-map]
|
2016-06-05 15:39:58 +00:00
|
|
|
(let [results (transform [ALL LAST]
|
|
|
|
|
(fn [afn]
|
2016-06-05 19:25:00 +00:00
|
|
|
(average-time-ms 8 amt-per-iter afn))
|
2016-06-05 15:39:58 +00:00
|
|
|
afn-map)
|
|
|
|
|
[[_ best-time] & _ :as sorted] (sort-by last results)
|
|
|
|
|
]
|
|
|
|
|
(println "\nAvg(ms)\t\tvs best\t\tCode")
|
|
|
|
|
(doseq [[k t] sorted]
|
|
|
|
|
(println (pretty-float5 t) "\t\t" (pretty-float3 (/ t best-time 1.0)) "\t\t" k)
|
|
|
|
|
)))
|
|
|
|
|
|
2016-06-05 19:25:00 +00:00
|
|
|
(defmacro run-benchmark [name amt-per-iter & exprs]
|
2016-06-07 00:29:45 +00:00
|
|
|
(let [afn-map (->> exprs shuffle (map (fn [e] [`(quote ~e) `(fn [] ~e)])) (into {}))]
|
2016-06-05 15:39:58 +00:00
|
|
|
`(do
|
|
|
|
|
(println "Benchmark:" ~name)
|
2016-06-05 19:25:00 +00:00
|
|
|
(compare-benchmark ~amt-per-iter ~afn-map)
|
2016-06-05 15:39:58 +00:00
|
|
|
(println "\n********************************\n")
|
|
|
|
|
)))
|
|
|
|
|
|
|
|
|
|
(let [data {:a {:b {:c 1}}}
|
|
|
|
|
p (comp-paths :a :b :c)]
|
2016-06-05 19:25:00 +00:00
|
|
|
(run-benchmark "get value in nested map" 10000000
|
2016-06-07 00:29:45 +00:00
|
|
|
(select-any [:a :b :c] data)
|
2016-06-07 14:18:20 +00:00
|
|
|
(select-one [:a :b :c] data)
|
|
|
|
|
(select-first [:a :b :c] data)
|
|
|
|
|
(select-one! [:a :b :c] data)
|
2016-06-07 00:29:45 +00:00
|
|
|
(compiled-select-any p data)
|
2016-06-05 15:39:58 +00:00
|
|
|
(get-in data [:a :b :c])
|
2016-06-07 00:29:45 +00:00
|
|
|
(-> data :a :b :c)
|
|
|
|
|
))
|
2016-06-05 15:39:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
;; because below 1.7 there is no update function
|
|
|
|
|
(defn- my-update [m k afn]
|
|
|
|
|
(assoc m k (afn (get m k))))
|
|
|
|
|
|
|
|
|
|
(defn manual-transform [m afn]
|
|
|
|
|
(my-update m :a
|
|
|
|
|
(fn [m2]
|
|
|
|
|
(my-update m2 :b
|
|
|
|
|
(fn [m3]
|
|
|
|
|
(my-update m3 :c afn))))))
|
|
|
|
|
|
|
|
|
|
(let [data {:a {:b {:c 1}}}]
|
2016-06-05 19:25:00 +00:00
|
|
|
(run-benchmark "update value in nested map" 1000000
|
2016-06-05 15:39:58 +00:00
|
|
|
(update-in data [:a :b :c] inc)
|
|
|
|
|
(transform [:a :b :c] inc data)
|
|
|
|
|
(manual-transform data inc)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
(let [data [1 2 3 4 5]]
|
2016-06-05 19:25:00 +00:00
|
|
|
(run-benchmark "map a function over a vector" 1000000
|
2016-06-05 15:39:58 +00:00
|
|
|
(vec (map inc data))
|
|
|
|
|
(mapv inc data)
|
|
|
|
|
(transform ALL inc data)
|
|
|
|
|
))
|
|
|
|
|
|
2016-06-07 00:29:45 +00:00
|
|
|
(let [data [1 2 3 4 5 6 7 8 9 10]]
|
|
|
|
|
(run-benchmark "filter a sequence" 1000000
|
|
|
|
|
(doall (filter even? data))
|
2016-06-07 04:49:52 +00:00
|
|
|
(filterv even? data)
|
2016-06-07 00:29:45 +00:00
|
|
|
(select [ALL even?] data)
|
2016-06-07 01:30:14 +00:00
|
|
|
(select-any (filterer even?) data)
|
2016-06-07 00:29:45 +00:00
|
|
|
))
|
|
|
|
|
|
2016-06-07 13:40:14 +00:00
|
|
|
(let [data [{:a 2 :b 2} {:a 1} {:a 4} {:a 6}]]
|
|
|
|
|
(run-benchmark "even :a values from sequence of maps" 1000000
|
|
|
|
|
(select [ALL :a even?] data)
|
|
|
|
|
(->> data (mapv :a) (filter even?) doall)
|
|
|
|
|
))
|
|
|
|
|
|
2016-06-07 20:07:01 +00:00
|
|
|
(let [v (vec (range 1000))]
|
|
|
|
|
(run-benchmark "END on large vector"
|
|
|
|
|
5000000
|
|
|
|
|
(setval END [1] v)
|
|
|
|
|
(reduce conj v [1])
|
|
|
|
|
(conj v 1)))
|
2016-06-05 15:39:58 +00:00
|
|
|
|
2016-06-05 16:02:24 +00:00
|
|
|
(defn- update-pair [[k v]]
|
|
|
|
|
[k (inc v)])
|
|
|
|
|
|
|
|
|
|
(defn manual-similar-reduce-kv [data]
|
|
|
|
|
(reduce-kv
|
|
|
|
|
(fn [m k v]
|
|
|
|
|
(let [[newk newv] (update-pair [k v])]
|
|
|
|
|
(assoc m newk newv)))
|
|
|
|
|
{}
|
|
|
|
|
data
|
|
|
|
|
))
|
|
|
|
|
|
2016-06-05 15:39:58 +00:00
|
|
|
(let [data {:a 1 :b 2 :c 3 :d 4}]
|
2016-06-05 19:25:00 +00:00
|
|
|
(run-benchmark "transform values of a map" 1000000
|
2016-06-05 15:39:58 +00:00
|
|
|
(into {} (for [[k v] data] [k (inc v)]))
|
|
|
|
|
(reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
|
2016-06-05 16:02:24 +00:00
|
|
|
(manual-similar-reduce-kv data)
|
2016-06-05 15:39:58 +00:00
|
|
|
(transform [ALL LAST] inc data)
|
2016-06-06 20:03:08 +00:00
|
|
|
(transform MAP-VALS inc data)
|
2016-06-05 15:39:58 +00:00
|
|
|
))
|
|
|
|
|
|
2016-06-05 19:25:00 +00:00
|
|
|
(let [data (->> (for [i (range 1000)] [i i]) (into {}))]
|
|
|
|
|
(run-benchmark "transform values of large map" 1000
|
|
|
|
|
(into {} (for [[k v] data] [k (inc v)]))
|
|
|
|
|
(reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
|
|
|
|
|
(manual-similar-reduce-kv data)
|
|
|
|
|
(transform [ALL LAST] inc data)
|
2016-06-06 20:03:08 +00:00
|
|
|
(transform MAP-VALS inc data)
|
2016-06-05 19:25:00 +00:00
|
|
|
))
|
2016-06-05 15:39:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
(declarepath TreeValues)
|
|
|
|
|
|
|
|
|
|
(providepath TreeValues
|
|
|
|
|
(if-path vector?
|
|
|
|
|
[ALL TreeValues]
|
|
|
|
|
STAY))
|
|
|
|
|
|
|
|
|
|
(defprotocolpath TreeValuesProt)
|
|
|
|
|
|
|
|
|
|
(extend-protocolpath TreeValuesProt
|
|
|
|
|
clojure.lang.PersistentVector [ALL TreeValuesProt]
|
|
|
|
|
Object STAY)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defn tree-value-transform [afn atree]
|
|
|
|
|
(if (vector? atree)
|
|
|
|
|
(mapv #(tree-value-transform afn %) atree)
|
|
|
|
|
(afn atree)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
(let [data [1 2 [[3]] [4 6 [7 [8]] 10]]]
|
|
|
|
|
(run-benchmark "update every value in a tree (represented with vectors)"
|
|
|
|
|
100000
|
|
|
|
|
(walk/postwalk (fn [e] (if (and (number? e) (even? e)) (inc e) e)) data)
|
|
|
|
|
(transform [(walker number?) even?] inc data)
|
|
|
|
|
(transform [TreeValues even?] inc data)
|
|
|
|
|
(transform [TreeValuesProt even?] inc data)
|
|
|
|
|
(tree-value-transform (fn [e] (if (even? e) (inc e) e)) data)
|
|
|
|
|
))
|
|
|
|
|
|