http://clojure.github.io/clojure-contrib/dataflow-api.html links to http://dev.clojure.org/display/community/Where+Did+Clojure.Contrib+Go which is 403
is a migrated version
docs updated
Hmm.. clojure.core/merge
seems quite slow. Is this worth investigating or a known issue?
โ ~ clj -Sdeps '{:deps {criterium {:mvn/version "0.4.5"}}, :jvm-opts ["-server"]}'
Clojure 1.10.0
user=> (require '[criterium.core :as cc])
nil
user=> (cc/quick-bench (merge {} {}))
Evaluation count : 2712492 in 6 samples of 452082 calls.
Execution time mean : 221.103323 ns
Execution time std-deviation : 1.892978 ns
Execution time lower quantile : 219.218989 ns ( 2.5%)
Execution time upper quantile : 223.419465 ns (97.5%)
Overhead used : 1.964440 ns
@ikitommi is reduce into
any faster?
maybe this calls for a library with a faster version of merge ๐
quite the speedup
maybe include coercing from and to a Clojure map since that's what you want eventually I guess?
yeh, this is not the wayโฆ
;; 230ns
(let [m1 {}
m2 {}]
(cc/quick-bench
(clojure.lang.PersistentArrayMap/create
(.toMap (.merge (Map/from ^java.util.Map m1)
(Map/from ^java.util.Map m2)
Maps/MERGE_LAST_WRITE_WINS)))))
merge flamegraph
A merge of an empty map in to an empty map is basically the worst case to micro benchmark, what would make it faster is checking for empty maps at the beginning and doing nothing, which wouldn't speed up any real world use cases
I guess you could gain some speed up by introducing multiple arities for the low arity cases.
(merge {} {})
is (conj {} {})
, no need to start the full reduce1
machinery for that.
user> (cc/quick-bench (merge {} {}))
Evaluation count : 2421150 in 6 samples of 403525 calls.
Execution time mean : 250.990304 ns
Execution time std-deviation : 9.293609 ns
Execution time lower quantile : 243.127947 ns ( 2.5%)
Execution time upper quantile : 266.075269 ns (97.5%)
Overhead used : 2.076286 ns
Found 1 outliers in 6 samples (16.6667 %)
low-severe 1 (16.6667 %)
Variance from outliers : 13.8889 % Variance is moderately inflated by outliers
;; => nil
user> (defn unrolled-merge
([m1] m1)
([m1 m2]
(conj (or m1 {}) m2))
([m1 m2 & ms]
(#'clojure.core/reduce1 conj (merge m1 m2) ms)))
;; => #'user/unrolled-merge
user> (cc/quick-bench (unrolled-merge {} {}))
Evaluation count : 7737186 in 6 samples of 1289531 calls.
Execution time mean : 75.288592 ns
Execution time std-deviation : 1.673650 ns
Execution time lower quantile : 73.961836 ns ( 2.5%)
Execution time upper quantile : 78.025617 ns (97.5%)
Overhead used : 2.076286 ns
Found 1 outliers in 6 samples (16.6667 %)
low-severe 1 (16.6667 %)
Variance from outliers : 13.8889 % Variance is moderately inflated by outliers
;; => nil
user>
https://ask.clojure.org/index.php/2770/enhance-the-performance-of-map-merges?show=2770#q2770
The ticket is here:
The approach taken is to introduce merge1
which doesnโt use reduce1
, and then reimplement merge
later when the proper reduce
is available.
In my case, it was a real-world merge of small maps. But the time it takes was about the same as with empty maps, just to highlight the issue. Last comments from 2017: "This ticket needs help. Step 0 is writing a benchmark harness that exercises maps of various sizes, and the various polymorphic arguments below.".