Liking cljdoc? Tell your friends :D

Arraymap and hashmap merging benchmarks

Can two hashmaps be merged faster than Clojure's core merge and merge-with?

Observations: Clojure's merge is difficult to beat for hashmaps containing up to 100 key/value pairs. For larger hashmaps, custom implementations using reduce-kv and transients offer 30-40% shorter evaluation times.

clojure.core/merge ultimately delegates to conj buried within a first/next recursive implementation. Also, merge carries with it the variadic machinery to handle more than two hashmaps, some of which could be nil. This is an elegant, general implementation, but perhaps not as efficient as could be if we're in a situation where we know we'll always have exactly two non-nil hashmaps. One example of such a situation is building up an output hashmap in a multi-threaded transducing process.

We can mix and match tactics involving transients, transducers, etc., to see if we can harvest any noticeable performance improvement. So let's run some benchmarks to see if any particular tactic performs objectively faster than the others. Benchmarks are defined here. We'll run benchmarks varying

  • Underlying implementation functions (i.e., different compositions of conj, into, transient, etc.).
  • Size of the input hashmaps (0 to 16 key/value pairs, and 1 to 1 million key/value pairs).
  • merge-with functions (basic addition versus an extended series of arithmetic operations).
In every measurement, the pair of hashmaps to be merged share half their keys, so the final output hashmap contains one-third elements from the left input hashmap, and one-third elements uniquely from the right hashmap, and one-third elements from the right hashmap that 'overwrote' the left's because they shared keys.

As always with JVM benchmarks, don't stare too long at any one data point. Just get a sense for overall trends and the relative magnitudes. Single-digit percentage differences probably aren't significant.

Overall, we make three broad observations.

  • For merging smallish hashmaps, Clojure's core merge is difficult to beat. We see the best improvement using transduce and conj, giving 7-14% shorter completion times.

  • For merging larger hashmaps, we obtain 30-50% shorter times with a straightforward implementation using reduce-kv and transients.

  • No custom implementation beats merge-with for small hashmaps.

  • A custom implementation of merge-with using reduce-kv offers 10-20% shorter times.

  • A two-layer merge using reduce-kv outperforms the community's deep-merge by 35-50%.

Within the limits of these benchmarks, it does appear that we can write a trio of hashmap merging utilities that complete their tasks in noticeably shorter times. The Smoosh library provides that trio as a dependency.

2-layer merging

These benchmarks measure the performance of deep merge variants: functions that combine two hashmaps with descendant hashmaps associated to their top-level keys. These particular tests involve merging only two layers deep, although most implementations would operate to an arbitrary depth. Clojure does not provide a deep-merge function, but CLJ-1468 (gold diamonds), a long-standing, un-accepted proposed implementation, serves as a handy baseline. We also tested several third-party variants.

A straightforward implementation using reduce-kv and transients, named merge-merge below, performed the best (i.e., shortest times), being nearly twice as fast as the baseline deep-merge, particularly for large hashmaps containing one-million elements.

Note: Logarithmic axes.

(fn [n] ((two-layer-merge-tactic) ((map-of-maps n) :left) ((map-of-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((two-layer-merge-tactic) ((map-of-maps n) :left) ((map-of-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 1 10 100 1000 10000 100000 1000000
clj-deep-merge 1.4e-04±2.3e-06 1.7e-04±5.5e-06 4.5e-04±5.2e-06 3.7e-03±6.5e-05 3.5e-02±8.6e-04 3.7e-01±5.1e-02 4.3e+00±2.5e-01
deep-merge-CLJ-1468 1.3e-04±7.4e-07 1.5e-04±7.6e-07 4.0e-04±2.7e-06 3.2e-03±9.5e-05 3.0e-02±1.7e-04 3.2e-01±5.2e-02 3.8e+00±3.1e-01
kitchensink 1.3e-04±1.7e-06 1.5e-04±8.8e-07 3.9e-04±2.0e-06 2.9e-03±2.5e-05 3.0e-02±2.3e-04 3.1e-01±5.3e-02 3.7e+00±2.5e-01
medley 1.3e-04±8.3e-07 1.5e-04±1.1e-06 3.2e-04±1.2e-06 2.3e-03±1.6e-05 2.2e-02±2.2e-04 2.2e-01±3.5e-03 2.9e+00±2.2e-01
merge-merge 1.4e-04±3.6e-06 1.5e-04±2.6e-06 2.6e-04±1.2e-06 1.5e-03±1.4e-05 1.5e-02±1.8e-04 1.4e-01±4.3e-04 2.2e+00±2.4e-01
useful 1.3e-04±2.5e-06 1.6e-04±2.9e-06 4.0e-04±3.9e-05 2.9e-03±4.9e-04 2.7e-02±1.3e-04 2.7e-01±3.1e-03 3.4e+00±2.7e-01

Merge tactics for large hashmaps

Using the same cohort of merge tactics as the small hashmaps benchmarks, we tested input hashmaps ranging up to one-million key/value pairs. In general, we see that larger hashmaps require longer to merge. Most tactics perform similarly up to one-hundred key/value pairs. For inputs larger than that, a handful of alternative implementations notably beat Clojure's merge. In broad strokes, merge functions built with reduce-kv along with transients can provide nearly a 50% reduction in evaluation time.

Note that the log-log axes may visually diminish the differences. Tap the 'Show details' button to see the recorded data, particularly the rightmost column displaying the evaluation times for merging a pair of one-million element hashmaps.

(fn [n] ((merge-tactic) ((large-maps n) :left) ((large-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-tactic) ((large-maps n) :left) ((large-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 1 10 100 1000 10000 100000 1000000
conj-merge 1.3e-04±3.4e-07 1.3e-04±8.6e-07 1.4e-04±9.4e-07 3.3e-04±2.4e-06 2.8e-03±3.8e-05 2.8e-02±1.7e-03 4.2e-01±6.1e-02
core-merge 1.3e-04±4.9e-07 1.3e-04±7.4e-07 1.5e-04±1.4e-06 3.5e-04±3.4e-06 2.9e-03±6.4e-05 2.8e-02±2.1e-03 4.2e-01±6.5e-02
fast-map-merge 1.3e-04±1.4e-06 1.3e-04±5.4e-07 1.4e-04±1.1e-06 2.9e-04±3.5e-06 2.1e-03±3.3e-05 2.2e-02±1.6e-03 3.4e-01±7.3e-02
into-merge 1.3e-04±4.3e-07 1.3e-04±1.6e-06 1.4e-04±6.7e-07 2.8e-04±1.5e-06 1.6e-03±1.6e-05 1.9e-02±6.1e-05 2.9e-01±5.2e-03
reduce-conj-merge 1.4e-04±9.1e-07 1.4e-04±6.1e-07 1.6e-04±6.3e-07 3.5e-04±1.6e-06 2.8e-03±3.8e-05 2.7e-02±1.3e-04 4.0e-01±7.8e-02
reduce-conj-transient-merge 1.3e-04±1.3e-06 1.3e-04±5.2e-07 1.4e-04±4.9e-07 2.9e-04±2.3e-06 1.8e-03±2.9e-05 1.9e-02±4.4e-05 2.8e-01±3.4e-03
reduce-kv-assoc-merge 1.2e-04±6.5e-07 1.3e-04±1.2e-06 1.4e-04±6.2e-07 2.7e-04±1.8e-06 2.0e-03±9.3e-06 2.0e-02±1.2e-03 3.6e-01±1.0e-01
reduce-kv-assoc-transient-merge 1.3e-04±4.4e-07 1.3e-04±8.9e-07 1.4e-04±5.0e-06 2.5e-04±8.4e-07 1.5e-03±1.0e-04 1.5e-02±3.6e-04 2.4e-01±8.0e-03
rmerge! 1.2e-04±6.0e-07 1.2e-04±6.7e-07 1.4e-04±6.9e-07 2.7e-04±8.9e-07 1.6e-03±9.6e-06 1.7e-02±1.5e-04 2.3e-01±1.1e-03
switched-reduce-kv-merge 1.3e-04±4.6e-07 1.4e-04±3.3e-07 1.5e-04±2.4e-07 2.6e-04±1.7e-06 1.5e-03±5.2e-05 1.4e-02±5.1e-05 2.3e-01±2.5e-03
transducer-conj-merge 1.2e-04±7.4e-07 1.2e-04±5.7e-07 1.4e-04±8.2e-07 3.2e-04±2.3e-06 2.5e-03±7.3e-05 2.6e-02±1.9e-04 3.9e-01±6.7e-02
transducer-into-merge 1.3e-04±4.1e-07 1.3e-04±5.6e-07 1.4e-04±1.2e-06 2.9e-04±9.1e-07 1.8e-03±1.7e-05 2.0e-02±8.4e-05 2.9e-01±1.1e-03

Merge tactics for small arraymaps/hashmaps

We'll consider twelve merging tactics, some pulled in from third-party libraries. There's nothing terribly special about any particular implementation. They're each just a different riff on combining standard Clojure functions (reduce, conj, assoc, etc.) in different ways.

We see that execution times are very weakly correlated with input hashmap size up to sixteen elements. We can also see a small bump around five key/value pairs, where the final output requires promoting an arraymap to a hashmap. Clojure's core merge (gold diamonds) performed about the middle of the pack. A handful of implementations are able to perform mildly better (i.e., lower times by <10%).

(fn [n] ((merge-tactic) ((small-maps n) :left) ((small-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-tactic) ((small-maps n) :left) ((small-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
conj-merge 1.4e-04±3.7e-06 1.3e-04±9.2e-07 1.3e-04±1.5e-06 1.3e-04±3.7e-06 1.3e-04±5.5e-07 1.3e-04±5.2e-07 1.3e-04±8.1e-07 1.3e-04±7.2e-07 1.3e-04±5.0e-07 1.3e-04±3.7e-06 1.3e-04±2.5e-06 1.3e-04±4.3e-07 1.3e-04±1.5e-06 1.3e-04±6.3e-07 1.3e-04±5.5e-07 1.3e-04±7.5e-07 1.3e-04±8.1e-07
core-merge 1.3e-04±9.5e-07 1.3e-04±7.4e-07 1.3e-04±8.7e-07 1.3e-04±4.1e-07 1.3e-04±1.6e-07 1.3e-04±1.5e-06 1.3e-04±8.2e-07 1.3e-04±6.9e-07 1.3e-04±8.0e-07 1.3e-04±5.1e-07 1.3e-04±1.2e-06 1.3e-04±5.6e-07 1.3e-04±1.2e-06 1.3e-04±7.7e-07 1.3e-04±4.6e-07 1.3e-04±4.6e-07 1.3e-04±4.0e-07
fast-map-merge 1.3e-04±1.1e-06 1.3e-04±2.1e-06 1.2e-04±3.9e-07 1.3e-04±1.7e-06 1.3e-04±2.6e-06 1.2e-04±4.6e-07 1.3e-04±5.7e-07 1.3e-04±6.5e-07 1.3e-04±3.4e-07 1.3e-04±2.3e-06 1.3e-04±2.1e-06 1.3e-04±4.8e-07 1.3e-04±4.9e-07 1.3e-04±1.4e-06 1.3e-04±7.5e-07 1.3e-04±3.9e-07 1.3e-04±6.1e-07
into-merge 1.3e-04±1.0e-06 1.3e-04±2.4e-06 1.4e-04±1.8e-06 1.3e-04±4.1e-07 1.3e-04±2.1e-06 1.3e-04±1.0e-06 1.3e-04±1.0e-06 1.3e-04±1.1e-06 1.3e-04±8.1e-07 1.3e-04±1.1e-06 1.3e-04±6.7e-07 1.3e-04±8.2e-07 1.3e-04±5.4e-07 1.3e-04±6.9e-07 1.3e-04±1.2e-06 1.3e-04±1.6e-06 1.3e-04±9.1e-07
reduce-conj-merge 1.5e-04±1.4e-05 1.4e-04±5.1e-07 1.4e-04±3.9e-07 1.4e-04±2.3e-06 1.4e-04±2.7e-06 1.4e-04±1.7e-06 1.5e-04±3.5e-06 1.4e-04±1.9e-06 1.4e-04±1.4e-06 1.4e-04±1.1e-06 1.4e-04±8.4e-07 1.4e-04±1.8e-06 1.4e-04±5.2e-07 1.4e-04±1.7e-06 1.5e-04±3.7e-06 1.4e-04±1.2e-06 1.4e-04±3.3e-07
reduce-conj-transient-merge 1.2e-04±8.5e-07 1.2e-04±1.0e-06 1.3e-04±8.7e-07 1.3e-04±1.1e-06 1.2e-04±6.0e-07 1.3e-04±1.6e-06 1.3e-04±1.0e-06 1.3e-04±3.7e-07 1.3e-04±4.7e-07 1.3e-04±6.6e-07 1.3e-04±1.0e-06 1.3e-04±1.1e-06 1.3e-04±4.4e-07 1.3e-04±6.7e-07 1.3e-04±8.9e-07 1.3e-04±6.5e-07 1.3e-04±6.2e-07
reduce-kv-assoc-merge 1.2e-04±2.4e-06 1.2e-04±6.1e-07 1.2e-04±8.2e-07 1.3e-04±1.1e-06 1.2e-04±2.1e-07 1.2e-04±1.9e-07 1.3e-04±2.4e-06 1.3e-04±8.4e-07 1.3e-04±8.0e-07 1.3e-04±8.8e-07 1.3e-04±3.9e-07 1.3e-04±6.0e-07 1.3e-04±1.2e-06 1.3e-04±1.7e-06 1.3e-04±9.4e-07 1.3e-04±2.9e-07 1.3e-04±7.3e-07
reduce-kv-assoc-transient-merge 1.3e-04±1.5e-06 1.3e-04±4.4e-06 1.3e-04±2.7e-06 1.4e-04±2.1e-06 1.3e-04±4.2e-07 1.3e-04±2.2e-07 1.3e-04±5.0e-07 1.3e-04±4.0e-07 1.3e-04±8.2e-07 1.4e-04±1.7e-06 1.3e-04±8.7e-07 1.3e-04±3.7e-07 1.4e-04±2.6e-06 1.3e-04±9.8e-07 1.4e-04±3.6e-06 1.4e-04±1.6e-06 1.4e-04±1.9e-06
rmerge! 1.2e-04±2.2e-06 1.2e-04±7.6e-07 1.2e-04±6.0e-07 1.2e-04±7.4e-07 1.2e-04±1.4e-06 1.2e-04±5.7e-07 1.2e-04±7.1e-07 1.2e-04±8.4e-07 1.2e-04±5.8e-07 1.2e-04±8.4e-07 1.2e-04±8.8e-07 1.2e-04±1.2e-06 1.2e-04±5.9e-07 1.2e-04±4.6e-07 1.2e-04±8.1e-07 1.2e-04±5.5e-07 1.2e-04±6.0e-07
switched-reduce-kv-merge 1.3e-04±4.1e-07 1.3e-04±4.7e-07 1.3e-04±3.7e-07 1.3e-04±4.0e-07 1.3e-04±4.7e-07 1.3e-04±1.1e-06 1.4e-04±5.7e-07 1.4e-04±1.5e-06 1.4e-04±6.6e-07 1.4e-04±6.7e-07 1.4e-04±5.3e-07 1.4e-04±1.8e-06 1.4e-04±2.0e-06 1.4e-04±2.5e-07 1.4e-04±7.1e-07 1.4e-04±4.1e-07 1.4e-04±3.3e-07
transducer-conj-merge 1.2e-04±1.2e-06 1.2e-04±1.5e-06 1.2e-04±4.9e-07 1.2e-04±6.8e-07 1.2e-04±4.2e-07 1.2e-04±7.5e-07 1.2e-04±6.7e-07 1.2e-04±2.2e-07 1.2e-04±4.6e-07 1.2e-04±1.0e-06 1.2e-04±2.7e-07 1.2e-04±9.5e-07 1.2e-04±5.3e-07 1.2e-04±2.4e-07 1.2e-04±8.5e-07 1.2e-04±4.3e-07 1.2e-04±3.5e-07
transducer-into-merge 1.3e-04±3.7e-06 1.2e-04±2.7e-07 1.2e-04±5.7e-07 1.3e-04±2.0e-06 1.2e-04±9.9e-07 1.2e-04±9.4e-07 1.3e-04±1.2e-06 1.3e-04±8.4e-07 1.3e-04±8.8e-07 1.3e-04±1.3e-06 1.3e-04±5.8e-07 1.3e-04±9.1e-07 1.3e-04±3.5e-07 1.3e-04±1.5e-06 1.3e-04±7.4e-07 1.3e-04±6.4e-07 1.3e-04±2.4e-07

Merge-with addition tactics for large hashmaps

These benchmarks extend the merging-with tests to hashmaps up to one-million elements. Unlike with the smaller hashmaps, where the results are fairly indistinguishable, the merge-with variants perform noticeably different with larger hashmaps.

For example, the implementation using reduce-kv (green triangles) offers 10-20% shorter evaluation times, with the improvement increasing as the inputs grow larger.

(fn [n] ((merge-with-tactic) + ((large-maps n) :left) ((large-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-with-tactic) + ((large-maps n) :left) ((large-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 1 10 100 1000 10000 100000 1000000
core-merge-with 1.3e-04±8.0e-07 1.3e-04±5.8e-07 1.6e-04±2.4e-06 4.8e-04±2.4e-06 4.1e-03±3.6e-05 4.2e-02±3.1e-04 6.2e-01±8.0e-02
reduce-assoc-merge-with 1.3e-04±6.5e-07 1.3e-04±9.1e-07 1.6e-04±2.3e-06 4.5e-04±1.7e-06 3.8e-03±1.6e-05 3.9e-02±2.5e-04 5.3e-01±6.5e-02
reduce-assoc-transient-merge-with 1.3e-04±1.1e-06 1.4e-04±5.6e-07 1.7e-04±9.3e-07 5.8e-04±4.9e-06 4.8e-03±9.3e-05 4.9e-02±4.8e-04 5.7e-01±5.4e-03
reduce-kv-assoc-merge-with 1.3e-04±3.5e-07 1.3e-04±2.7e-07 1.5e-04±5.6e-07 4.1e-04±1.0e-06 3.6e-03±2.1e-05 3.6e-02±1.5e-04 4.9e-01±7.0e-02
reduce-kv-assoc-transient-merge-with 1.4e-04±3.5e-06 1.4e-04±8.5e-07 1.7e-04±1.4e-06 5.3e-04±2.2e-06 4.3e-03±1.3e-04 4.3e-02±2.9e-04 5.2e-01±2.0e-03

Merge-with addition tactics for small hashmaps

These benchmarks measure merging small hashmaps (sixteen elements or fewer) using merge-with behavior, simple addition. Clojure's implementation is pretty much the fastest, but all variants cluster within a few percent. But see what happens when the hashmaps are larger.

(fn [n] ((merge-with-tactic) + ((small-maps n) :left) ((small-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-with-tactic) + ((small-maps n) :left) ((small-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
core-merge-with 1.3e-04±4.1e-07 1.3e-04±3.5e-07 1.3e-04±7.4e-07 1.3e-04±7.3e-07 1.3e-04±3.0e-07 1.3e-04±9.9e-07 1.3e-04±6.3e-07 1.3e-04±5.3e-07 1.3e-04±8.6e-07 1.3e-04±8.7e-07 1.3e-04±6.3e-07 1.3e-04±8.1e-07 1.3e-04±6.7e-07 1.3e-04±8.4e-07 1.4e-04±8.8e-07 1.3e-04±6.6e-07 1.3e-04±8.6e-07
reduce-assoc-merge-with 1.3e-04±9.5e-07 1.3e-04±1.6e-06 1.3e-04±6.9e-07 1.3e-04±4.2e-07 1.3e-04±7.3e-07 1.3e-04±8.8e-07 1.3e-04±1.0e-06 1.3e-04±9.8e-07 1.3e-04±9.6e-07 1.3e-04±7.7e-07 1.3e-04±6.5e-07 1.3e-04±8.3e-07 1.3e-04±6.2e-07 1.3e-04±9.3e-07 1.3e-04±1.5e-06 1.3e-04±7.4e-07 1.3e-04±8.7e-07
reduce-assoc-transient-merge-with 1.3e-04±7.8e-07 1.4e-04±1.6e-06 1.3e-04±7.8e-07 1.3e-04±6.0e-07 1.3e-04±6.3e-07 1.3e-04±5.4e-07 1.4e-04±5.4e-07 1.4e-04±7.2e-07 1.4e-04±1.2e-06 1.4e-04±6.7e-07 1.4e-04±4.9e-07 1.4e-04±1.2e-06 1.4e-04±6.9e-07 1.4e-04±7.9e-07 1.4e-04±2.8e-07 1.4e-04±5.7e-07 1.4e-04±5.9e-07
reduce-kv-assoc-merge-with 1.3e-04±2.1e-06 1.3e-04±1.3e-06 1.3e-04±1.7e-06 1.3e-04±8.3e-07 1.4e-04±2.6e-06 1.3e-04±9.8e-07 1.3e-04±1.1e-06 1.3e-04±1.2e-06 1.4e-04±1.0e-06 1.4e-04±1.2e-06 1.3e-04±2.2e-06 1.3e-04±2.8e-07 1.4e-04±2.7e-06 1.3e-04±1.0e-06 1.3e-04±4.0e-07 1.3e-04±2.5e-06 1.3e-04±6.7e-07
reduce-kv-assoc-transient-merge-with 1.4e-04±1.2e-06 1.4e-04±6.0e-07 1.4e-04±8.7e-07 1.4e-04±1.2e-06 1.4e-04±8.0e-07 1.4e-04±3.9e-07 1.4e-04±9.3e-07 1.4e-04±2.1e-06 1.4e-04±1.1e-06 1.4e-04±8.5e-07 1.4e-04±5.1e-07 1.4e-04±6.3e-07 1.4e-04±8.0e-07 1.4e-04±1.6e-06 1.4e-04±5.7e-07 1.5e-04±1.4e-06 1.5e-04±7.8e-06

Merge-with extended arithmetic tactics for large hashmaps

These are the same benchmarks, merging with eighteen mathematical operations, but with hashmaps containing up to one-million elements. As with the simple addition tests, the implementation using reduce-kv offers 10-20% performance improvement (i.e., lower times) compared Clojure's built-in merge-with. The performance improvement increases as the inputs grow larger.

(fn [n] ((merge-with-tactic) extended ((large-maps n) :left) ((large-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-with-tactic) extended ((large-maps n) :left) ((large-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 1 10 100 1000 10000 100000 1000000
core-merge-with 1.3e-04±7.5e-07 1.3e-04±6.6e-07 1.8e-04±2.1e-07 6.4e-04±9.1e-06 5.8e-03±1.2e-05 5.8e-02±2.4e-04 7.8e-01±7.1e-02
reduce-assoc-merge-with 1.3e-04±8.1e-07 1.3e-04±1.1e-06 1.7e-04±2.5e-06 6.1e-04±2.0e-06 5.3e-03±9.0e-05 5.4e-02±1.9e-04 6.9e-01±6.2e-02
reduce-assoc-transient-merge-with 1.3e-04±1.2e-06 1.4e-04±8.1e-07 1.9e-04±1.0e-06 7.2e-04±4.4e-06 6.1e-03±1.2e-04 6.4e-02±3.6e-04 7.2e-01±4.3e-03
reduce-kv-assoc-merge-with 1.3e-04±1.1e-06 1.4e-04±5.8e-07 1.7e-04±3.9e-06 5.7e-04±1.5e-05 5.2e-03±2.7e-05 5.0e-02±1.6e-04 6.5e-01±7.8e-02
reduce-kv-assoc-transient-merge-with 1.4e-04±2.4e-06 1.5e-04±2.1e-06 1.9e-04±1.6e-06 7.1e-04±2.1e-05 6.0e-03±1.8e-04 5.8e-02±4.4e-04 7.2e-01±1.4e-02

Merge-with extended arithmetic tactics for small hashmaps

These benchmarks explore the performance implications of more expensive value combining. Similar to the earlier ones, but instead of a single addition operation, the values are merged with a series of eighteen mathematical operations, such as cosine, natural logarithm, etc.

As before, Clojure's core merge-with is among the best performers, but all variants perform similarly in absolute terms.

(fn [n] ((merge-with-tactic) extended ((small-maps n) :left) ((small-maps n) :right)))

Benchmark measurements for expression `(fn [n] ((merge-with-tactic) extended ((small-maps n) :left) ((small-maps n) :right)))`, time versus 'n' arguments, comparing different versions. Show details
times in seconds, mean±std
arg, n
version 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
core-merge-with 1.3e-04±8.1e-07 1.3e-04±4.4e-07 1.3e-04±5.5e-07 1.3e-04±5.7e-07 1.3e-04±7.7e-07 1.3e-04±9.8e-07 1.3e-04±2.2e-06 1.3e-04±6.7e-07 1.4e-04±1.2e-06 1.3e-04±4.1e-07 1.3e-04±4.1e-07 1.4e-04±2.9e-07 1.4e-04±6.5e-07 1.4e-04±9.1e-07 1.4e-04±5.2e-07 1.4e-04±5.5e-07 1.4e-04±8.0e-07
reduce-assoc-merge-with 1.3e-04±8.7e-07 1.3e-04±1.2e-06 1.3e-04±8.0e-07 1.3e-04±5.9e-07 1.3e-04±9.0e-07 1.3e-04±5.8e-07 1.3e-04±4.8e-07 1.3e-04±1.2e-06 1.4e-04±1.0e-06 1.3e-04±8.6e-07 1.3e-04±7.4e-07 1.4e-04±9.5e-07 1.3e-04±8.4e-07 1.3e-04±4.7e-07 1.4e-04±4.6e-07 1.4e-04±5.1e-07 1.4e-04±7.4e-07
reduce-assoc-transient-merge-with 1.3e-04±4.5e-07 1.3e-04±8.0e-07 1.3e-04±6.8e-07 1.3e-04±4.0e-07 1.4e-04±1.6e-06 1.4e-04±5.8e-07 1.5e-04±1.3e-06 1.4e-04±1.2e-06 1.4e-04±4.2e-07 1.4e-04±5.9e-07 1.6e-04±8.5e-06 1.4e-04±2.1e-06 1.4e-04±4.3e-07 1.5e-04±2.9e-06 1.4e-04±6.5e-07 1.5e-04±4.0e-07 1.5e-04±6.2e-07
reduce-kv-assoc-merge-with 1.3e-04±2.5e-07 1.3e-04±2.1e-07 1.3e-04±5.7e-07 1.3e-04±6.0e-07 1.3e-04±1.5e-06 1.4e-04±7.9e-07 1.3e-04±1.9e-06 1.4e-04±1.3e-06 1.5e-04±1.0e-05 1.4e-04±2.7e-06 1.3e-04±1.8e-07 1.3e-04±3.2e-07 1.3e-04±8.5e-07 1.4e-04±1.4e-06 1.4e-04±1.7e-06 1.4e-04±1.2e-06 1.4e-04±4.8e-07
reduce-kv-assoc-transient-merge-with 1.4e-04±1.1e-06 1.4e-04±1.3e-06 1.4e-04±1.9e-06 1.4e-04±6.9e-07 1.4e-04±6.1e-07 1.4e-04±8.8e-07 1.4e-04±8.3e-07 1.4e-04±7.9e-07 1.5e-04±4.4e-06 1.5e-04±2.3e-06 1.4e-04±1.7e-06 1.4e-04±2.0e-06 1.4e-04±2.9e-06 1.5e-04±3.9e-06 1.5e-04±1.6e-06 1.5e-04±5.8e-07 1.4e-04±1.2e-06

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close