Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Performance 奇怪的aget优化行为_Performance_Clojure_Clojure Java Interop - Fatal编程技术网

Performance 奇怪的aget优化行为

Performance 奇怪的aget优化行为,performance,clojure,clojure-java-interop,Performance,Clojure,Clojure Java Interop,关于这个问题的后续行动 在优化方面似乎有一些非常奇怪的事情。我们知道以下是事实: => (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 (

关于这个问题的后续行动

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

=> (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)
无反射警告,无需提示。通过将aget绑定到根变量或let,可以看到相同的行为

=> (let [f aget] (time (reduce + (for [x xa] (f xa x)))))
"Elapsed time: 43.912129 msecs"
4999950000

知道为什么绑定aget似乎“知道”如何优化,而核心函数却不知道吗?

当您通过高阶函数调用时,所有参数都转换为object。在这些情况下,编译器无法确定所调用函数的类型,因为在编译函数时它是未绑定的。只能确定它是可以用一些参数调用的东西。不会打印任何警告,因为任何东西都可以工作

user> (map aget (repeat xa) (range 100))
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99)
您已经找到了clojure编译器放弃的边缘,它只是将对象用于所有事情。(这是一个过于简单的解释)

如果您将其包装在自己编译的任何东西中(如匿名函数),那么警告将再次可见,尽管它们来自于编译匿名函数,而不是编译对映射的调用

user> (map #(aget %1 %2) (repeat xa) (range 100))
Reflection warning, NO_SOURCE_FILE:1 - call to aget can't be resolved.
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99)

然后,当一个类型提示被添加到匿名函数调用中时,警告就会消失,尽管这个函数调用是不变的

它与
aget
上的
:inline
指令有关,该指令扩展为
(.clojure.lang.RT(aget~a(int~i))
,而正常的函数调用涉及
反射器。请尝试以下操作:

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

类型的一个数量级优势。我不理解的是clojure编译器是否使用对象来访问总之,为什么性能几乎与类型提示版本相同,而与没有给出类型提示的最差性能版本不同?JVM中的热点编译器(对我来说)确实很奇怪对象的奇妙之处……最糟糕的情况是必须反思,在类中按名称查找每个函数,至少您可以避免:)我只是说调用作为参数传递的函数。在编译时调用的函数是固定的情况下(正常情况下),优化效果更好