Fork me on GitHub
#clojure-dev
<
2020-08-06
>
Vincent Cantin10:08:24

I noticed the implementation of clojure.core/update-in could me made simpler by not passing the f and args to the up function. Do you think it is worth changing it?

(defn update-in
  "'Updates' a value in a nested associative structure, where ks is a
  sequence of keys and f is a function that will take the old value
  and any supplied args and return the new value, and returns a new
  nested structure.  If any levels do not exist, hash-maps will be
  created."
  {:added "1.0"
   :static true}
  ([m ks f & args]
     (let [up (fn up [m ks f args]
                (let [[k & ks] ks]
                  (if ks
                    (assoc m k (up (get m k) ks f args))
                    (assoc m k (apply f (get m k) args)))))]
       (up m ks f args))))

Vincent Cantin10:08:58

It could be

(defn update-in
  "'Updates' a value in a nested associative structure, where ks is a
  sequence of keys and f is a function that will take the old value
  and any supplied args and return the new value, and returns a new
  nested structure.  If any levels do not exist, hash-maps will be
  created."
  {:added "1.0"
   :static true}
  ([m ks f & args]
   (let [up (fn up [m ks]
              (let [[k & ks] ks]
                (if ks
                  (assoc m k (up (get m k) ks f args))
                  (assoc m k (apply f (get m k) args)))))]
     (up m ks))))

mpenet11:08:20

@vincent.cantin did you profile it? I suspect with args coming from the closure the jvm might not be able to cache the up fn (it's pure in the original)

Vincent Cantin11:08:29

I did not profile it. You might be right.

mpenet11:08:59

you'd get 1 fn instance per call vs 1 for all update-in calls

Vincent Cantin11:08:17

Yes, it makes sense. Thank you.

Vincent Cantin14:08:26

It’s hard to see how the performance was improved. How did it happen?

Vincent Cantin14:08:49

Oh .. I see that the new version has 1 less apply

borkdude14:08:58

I’ve also seen benefits of avoiding apply in babashka. I wrote a macro to optimize 20 arities for this

borkdude14:08:26

I’ve tested your simplified version and didn’t really see a performance difference compared to core

👍 3
Ben Sless18:08:32

One thing I wonder about is why create an instance of up every time, instead of defing it as a private var? Wouldn't it save allocations and be able to be optimized by the JVM better?

borkdude18:08:55

@UK0810AQ2 Test it and you will know

Ben Sless18:08:19

Bringing up a REPL as we speak 🙂

Ben Sless19:08:11

now waiting for criterium to cook up an answer

Ben Sless19:08:08

Execution time mean : 328.707164 ns
vs.
Execution time mean : 384.654215 ns

Ben Sless19:08:19

what's going on here? 😮

devn05:08:55

This is an old joke in Clojure as I recall. “Don’t make Rich spell out 20+ arities again”