Clojure记录方法的差异

see*_*equ 3 performance record clojure

好吧,标题并不完全是我想要的,但它必须在记录的成员函数访问速度中找到一个有趣的东西.我将用这个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
Run Code Online (Sandbox Code Playgroud)

我无法真正看出这些差异的原因,所以我要求你这样做.

Leo*_*tny 5

第二次调用的问题是Clojure编译器无法r在编译时确定变量的类型,因此强制使用反射.

要避免它,您应该添加类型提示:

(let [^user.R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))
Run Code Online (Sandbox Code Playgroud)

或者干脆

(let [^R r (->R 1 2)] (time (dotimes [_ 100000] (.add r))))
Run Code Online (Sandbox Code Playgroud)

它将与Java风格的方法调用一样快.


如果要在代码中轻松诊断此类问题,请将*warn-on-reflection*flag 设置为true:

(set! *warn-on-reflection* true)
Run Code Online (Sandbox Code Playgroud)

或将其添加到文件中的:global-vars部分project.clj:

:global-vars {*warn-on-reflection* true}
Run Code Online (Sandbox Code Playgroud)

所以,正如你所看到的,没有反射方法调用比函数调用快一点.但反射可能会使方法调用变得非常缓慢.