2012-04-13 7 views
4

Followup по этому вопросу о aget performanceСтранное поведение оптимизации AGET

Там, кажется, что-то очень странное происходит по оптимизации мудр. Мы знали, что следующее верно:

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

нет отражения предупреждений, никаких подсказок, необходимых. Такое поведение наблюдается при привязке aget к корневому var или в let.

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

Любая идея, почему связанный aget, похоже, «знает», как оптимизировать, где нет функции ядра?

ответ

2

Это относится к директиве :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 + части, а не доступ к массиву. Этот пример устраняет это и показывает преимущество по порядку величины типа-намека, inlined aget.

1

При вызове функции более высокого порядка все аргументы передаются объекту. В этих случаях компилятор не может определить тип вызываемой функции, поскольку он сбрасывается при компиляции функции. Можно только определить, что это будет нечто, что можно вызвать с некоторыми аргументами. Предупреждение не печатается, потому что все будет работать.

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) 

, а затем предупреждение исчезает, когда подсказка типа добавляется к анонимному, хотя и неизменному вызову функции.

+0

То, что я не понимаю, - это то, что компилятор clojure использует объекты для всего, почему производительность почти идентична версии с намеченным типом, а не к наихудшей версии производительности, где не указаны подсказки типов? – NielsK

+0

Компилятор HotSpot в JVM делает странные (для меня) и замечательные вещи с объектами ... Худший случай - это отразить, чтобы найти каждую функцию по имени в своем классе, по крайней мере, вы избегаете этого :) –

+0

Я только говоря о вызове функции, которая была передана как параметр. оптимизация изображения лучше в тех случаях, когда вызываемая функция фиксируется во время компиляции («нормальный» случай) –

 Смежные вопросы

  • Нет связанных вопросов^_^