好的,标题并不是我想要的,但我必须在记录的成员函数访问速度中找到一个有趣的东西。我将通过此REPL会话进行说明:
==> (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
我无法真正看出这些差异的原因,所以我要求你这样做。
答案 0 :(得分:5)
第二次调用的问题是Clojure编译器无法在编译时确定r
变量的类型,因此强制使用反射。
要避免它,您应该添加type hint:
(let [^user.R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))
或只是
(let [^R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))
它和Java风格的方法调用一样快。
如果您想在代码中轻松诊断此类问题,请将*warn-on-reflection*
标志设置为true:
(set! *warn-on-reflection* true)
或将其添加到:global-vars
文件中的project.clj
部分:
:global-vars {*warn-on-reflection* true}
因此,正如您所看到的,没有反射方法调用比函数调用快一点。但反射可能会使方法调用变得非常缓慢。