taoensso / nippy

The fastest serialization library for Clojure
https://www.taoensso.com/nippy
Eclipse Public License 1.0
1.04k stars 60 forks source link

replace partial with an anonymous fn ((small) performance optimisation) #7

Closed mpenet closed 11 years ago

mpenet commented 11 years ago

Hi Peter,

It's a small detail but it seems partial induces a little overhead compared to an anonymous fns.

Some numbers for roundrip

partial : 13362 13346 13452

ano fn: 10988 10897 10951

The gain is not incredible, but it's there. Even tho I also find partial more elegant, it has a small cost and hotspot seems not to want to optimise that.

ptaoussanis commented 11 years ago

Hey, great catch - not a small difference at all! I'm surprised, I actually thought partial was faster than an anonymous fn. Not sure where that belief came from, I normally test such things.

Will go through my projects and look for other cases where I've made this mistake - thanks a lot!

mpenet commented 11 years ago

I tested them in isolation to be sure:


(def s [1])
(def p (partial conj s 2))
(def a #(conj s 2 %))

(dotimes [_ 5 ]
  (time (dotimes [i 1000000]
          (p 3)
          ;; (a 3)

          )))

The anonymous version is ~twice as fast over partial everytime.

On Sun, Jun 2, 2013 at 6:09 PM, Peter Taoussanis notifications@github.comwrote:

Hey, great catch - not a small difference at all! I'm surprised, I actually thought partial was faster than an anonymous fn. Not sure where that belief came from, I normally test such things.

Will go through my other projects and look for other cases where I've made this mistake - thanks a lot!

— Reply to this email directly or view it on GitHubhttps://github.com/ptaoussanis/nippy/pull/7#issuecomment-18808609 .

Max Penet


tel: +41 79 697 04 36 jabber: max.penet@gmail.com twitter: http://twitter.com/mpenet


mpenet commented 11 years ago

Awww it seems carmine can gain from that as well.

./src/taoensso/carmine/locks.clj:25:(def ^:private lkey (partial car/kname "carmine" "lock")) ./src/taoensso/carmine/protocol.clj:167: (utils/repeatedly* bulk-count (partial get-basic-reply! in ./src/taoensso/carmine/protocol.clj:197: (utils/mapv* (partial get-reply! in false) parsers))))) ./src/taoensso/carmine/message_queue.clj:23:(def qkey "Prefixed queue key" (memoize (partial car/kname "carmine" "mq")))

I guess the ones in protocol could indeed change a few things, I might send you a PR if you aren't quick enough! :)

ptaoussanis commented 11 years ago

Yeah, I just ran some tests to confirm your findings: seems to hold true across all Clojure versions and JVMs that I tested.

Have already updated Carmine ;-)

mpenet commented 11 years ago

And last detail, I also tested fn, it seems to be a tiny bit slower than #(), I guess it's the cost of checking for arg destructuring.

ptaoussanis commented 11 years ago

And last detail, I also tested fn, it seems to be a tiny bit slower than #(), I guess it's the cost of checking for arg destructuring.

I believe #() just expands to (fn* [] ()) though. Can't seem to reproduce any difference there on my end?

mpenet commented 11 years ago

yep, #() is the best choice here. By the way I think I found a couple more optimisations possible in carmine. testing them atm, but it's almost dinner time, you might get a PR in a few.

ptaoussanis commented 11 years ago

Great, heading to bed now - will take a look at any PRs in the morning. Cheers!