performance - 奇怪的 get 优化行为

标签 performance clojure clojure-java-interop

跟进这个关于 aget performance 的问题

在优化方面似乎有一些非常奇怪的事情。我们知道以下情况是正确的:

=> (def xa (int-array (range 100000)))
#'user/xa

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

=> (time (reduce + (for [x xa] (aget ^ints xa x))))
"Elapsed time: 42.80174 msecs"
4999950000

=> (time (reduce + (for [x xa] (aget xa x))))
"Elapsed time: 2067.673859 msecs"
4999950000
Reflection warning, NO_SOURCE_PATH:1 - call to aget can't be resolved.
Reflection warning, NO_SOURCE_PATH:1 - call to aget can't be resolved.

然而,一些进一步的实验真的让我很奇怪:
=> (for [f [get nth aget]] (time (reduce + (for [x xa] (f xa x)))))
("Elapsed time: 71.898128 msecs"
"Elapsed time: 62.080851 msecs"
"Elapsed time: 46.721892 msecs"
4999950000 4999950000 4999950000)

没有反射警告,不需要提示。通过将 get 绑定(bind)到根 var 或 let 可以看到相同的行为。
=> (let [f aget] (time (reduce + (for [x xa] (f xa x)))))
"Elapsed time: 43.912129 msecs"
4999950000

知道为什么绑定(bind)的 get 似乎“知道”如何优化,而核心功能不知道吗?

最佳答案

它与 :inline 有关关于 aget 的指令, 扩展为 (. clojure.lang.RT (aget ~a (int ~i)) ,而正常的函数调用涉及 Reflector .试试这些:

user> (time (reduce + (map #(clojure.lang.Reflector/prepRet 
       (.getComponentType (class xa)) (. java.lang.reflect.Array (get xa %))) xa)))
"Elapsed time: 63.484 msecs"
4999950000
user> (time (reduce + (map #(. clojure.lang.RT (aget xa (int %))) xa)))
Reflection warning, NO_SOURCE_FILE:1 - call to aget can't be resolved.
"Elapsed time: 2390.977 msecs"
4999950000

那么,您可能想知道内联的意义何在。好吧,看看这些结果:
user> (def xa (int-array (range 1000000))) ;; going to one million elements
#'user/xa
user> (let [f aget] (time (dotimes [n 1000000] (f xa n))))
"Elapsed time: 187.219 msecs"
user> (time (dotimes [n 1000000] (aget ^ints xa n)))
"Elapsed time: 8.562 msecs"

事实证明,在您的示例中,一旦您通过反射警告,您的新瓶颈就是 reduce +部分而不是数组访问。这个例子消除了这种情况,并显示了类型提示、内联 aget 的数量级优势。 .

关于performance - 奇怪的 get 优化行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10144937/

相关文章:

clojure - 如何标记 Clojure 函数以便可以通过 Java 反射识别它

sql - 能否在 Postgres 中有效地 LEFT OUTER JOIN 左表行的子集?

java - String 和 HashSet 之间用于检查重复项的最佳(性能 + 内存)是什么

java - 使用 Java 对象作为 Clojure 映射

clojure - 始终返回 nil 的 Clojure 函数

Clojure,尝试调用未绑定(bind)的 fn

Clojure 注释和整数

android - 在 Android 上提高 OpenGL ES 填充率的技巧

sql-server - 什么会导致 SQL Server 性能不佳?

clojure - 从 Clojure 中的文件打印和阅读列表