Difference in Clojure writing methods

Ok, the title is not exactly what I was looking for, but it should do, I found an interesting thing about the speed of access to the write member function. I will illustrate 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 cannot understand the reason for these differences, so I am asking this from you.

+3


source to share


1 answer


The problem with your second call is that the Clojure compiler cannot determine the type of the variable r

at compile time, so it is forced to use reflections.

To avoid this, you must add a hint type :

(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 will be as fast as a Java style method call.




If you want to easily diagnose such problems in your code, set the parameter *warn-on-reflection*

to true:

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

      

or add it to a section :global-vars

in the file project.clj

:

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

      




So, as you can see, without reflections, method calls are slightly faster than function calls. But reflections could make method calls very slow.

+5


source







All Articles