Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference in Clojure's record methods

Okay, the title is not exactly what I was looking for, but it has to do I found an interesting thing in the speed of record's member function access. I'll illustrate with this REPL session:

==> (defprotocol Add (add [_]))
Add
==> (defrecord R [x y] Add (add [_] (+ x y)))
=.R
==> (let [r (->R 1 2)] (time (dotimes [_ 100000] (add r)))) ; Pure functional style
"Elapsed time: 19.613694 msecs"
nil
==> (let [r (->R 1 2)] (time (dotimes [_ 100000] (.add r)))) ; Functional creation, but with method call
"Elapsed time: 477.29611 msecs"
nil
==> (let [r (R. 1 2)] (time (dotimes [_ 100000] (.add r)))) ; Java-style
"Elapsed time: 10.051506 msecs"
nil
==> (let [r (R. 1 2)] (time (dotimes [_ 100000] (add r)))) ; Java-style creation with functional call
"Elapsed time: 18.726801 msecs"
nil

I can't really see the reason for these differences, so I'm asking that from you.

like image 895
seequ Avatar asked May 14 '26 23:05

seequ


1 Answers

The problem with your second call is that Clojure compiler is unable to determine the type of r variable at compilation time, so it is forced to use reflections.

To avoid it you should add type hint:

(let [^user.R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))

or simply

(let [^R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))

and it'll be just as fast as Java-style method call.


If you want to easily diagnose such problems in your code, set *warn-on-reflection* flag to true:

(set! *warn-on-reflection* true)

or add it to :global-vars section in your project.clj file:

:global-vars {*warn-on-reflection* true}

So, as you can see, without reflections method calls are a little bit faster than functional calls. But reflections could make method calls really slow.

like image 193
Leonid Beschastny Avatar answered May 18 '26 12:05

Leonid Beschastny