spork icon indicating copy to clipboard operation
spork copied to clipboard

Faster get-in

Open joinr opened this issue 5 years ago • 0 comments

So the default clojure.core nested operations like get-in can be optimized if we have a path of literals known a-priori. We can compile that down to a sequence of lookups (preferably optimized .get ops on a java.util.Map or .valAt, or for broad generality, clojure.core/get). This avoids creating a vector at runtime, reducing over the vector, etc.

We can't do that in the case where values are passed in at runtime and we don't know the compile-time path (e.g. we have a path vector that includes symbols).

We can still kick clojure.core/get-in to the curb if we replace the default implementation with one that's slightly restrictive on types, in this case java.util.Map:

(defmacro get-in-map [m ks]
  (if (seq ks)
    `(let [^java.util.Map m# ~m]
       (if-let [res# (.get ^java.util.Map m#  ~(first ks))]
         (get-in-map res# ~(rest ks))))
  `~m))

The only restriction is that we provide a literal collection rather than a symbol for the path, but it works with normal clojure idioms.

user> (let [m {:a {:b {:c 3}}} x :c] (time (dotimes [i 1000000] (get-in m [:a :b x]))))
"Elapsed time: 116.3892 msecs"

user> (let [m {:a {:b {:c 3}}} x :c] (time (dotimes [i 1000000] (get-in-map m [:a :b x]))))
"Elapsed time: 28.9272 msecs"

user> (let [m {:a {:b {:c 3}}} x :c]  (get-in-map m [:a :b x]))
3

I dug this up since I overlooked get-in before when doing deep-assoc and friends, and I did not at the time know of how costly clojure.core/get is.

Oh, and unlike clojure.core/get-in, this variant is naturally short-circuiting if the path leads to nothing, so we get a bit more optimization (potentially a lot if there are deeply nested lookups):

user> (let [m {:a {:b {:c 3}}} x :d] (time (dotimes [i 1000000] (get-in m [:a x :c]))))
"Elapsed time: 119.1608 msecs"

user> (let [m {:a {:b {:c 3}}} x :d] (time (dotimes [i 1000000] (get-in-map m [:a x :c]))))
"Elapsed time: 19.3677 msecs"

joinr avatar Jan 29 '20 10:01 joinr