-->

奇怪的行为采取优化(Strange aget optimisation behavior)

2019-09-16 12:49发布

跟帖对这个问题皮亚杰性能

似乎有什么很奇怪的事情优化明智的。 我们知道下面是真实的:

=> (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.

然而,另外一些实验确实weirded我出去:

=> (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)

无反射警告,没有提示需要。 相同的行为是由皮亚杰结合到根VAR或在let看到。

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

任何想法,为什么绑定皮亚杰似乎“知道”如何优化,其中的核心功能不?

Answer 1:

它与做: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



Answer 2:

当您通过高阶函数调用的所有参数都转换为对象。 在这些情况下,编译器无法弄清楚该函数的类型被调用,因为当函数编译它是自由的。 它只能被确定,这将是东西,可以用一些参数来调用。 没有警告打印,因为任何事情会工作。

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)

然后报警消失时,一个类型提示添加到匿名的,但不变的,函数调用。



文章来源: Strange aget optimisation behavior