Clojure: why is aget so slow?

前端 未结 3 1519
一生所求
一生所求 2020-12-21 02:55

In my thinking, clojure vectors have a slight performance hit compared to java arrays. As a result I thought that \"conventional wisdom\" was that for those performance-crit

3条回答
  •  情书的邮戳
    2020-12-21 03:23

    it looks like reflection is washing out all your test's accuracy:

    user> (set! *warn-on-reflection* true)
    true
    user> (def x (vec (range 100000)))
    #'user/x
    user>  (def xa (int-array x))
    #'user/xa
    user>  (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ s (nth x i))) s)))
    NO_SOURCE_FILE:1 recur arg for primitive local: s is not matching primitive, had: Object, needed: long
    Auto-boxing loop arg: s
    "Elapsed time: 12.11893 msecs"
    4999950000
    user> (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ s (aget xa i))) s)))
    Reflection warning, NO_SOURCE_FILE:1 - call to aget can't be resolved.
    NO_SOURCE_FILE:1 recur arg for primitive local: s is not matching primitive, had: Object, needed: long
    Auto-boxing loop arg: s
    Reflection warning, NO_SOURCE_FILE:1 - call to aget can't be resolved.
    "Elapsed time: 2689.865468 msecs"
    4999950000
    user> 
    

    the second one just happens to have more reflection in it.

    When running this kind of benchmark be sure to run it many times to get the hotSpot compiler warmed up

    user> (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ s (aget xa i))) (long s))))
    "Elapsed time: 3135.651399 msecs"
    4999950000
    user> (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ (long s) (aget xa i))) (long s))))
    "Elapsed time: 1014.218461 msecs"
    4999950000
    user> (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ (long s) (aget xa i))) (long s))))
    "Elapsed time: 998.280869 msecs"
    4999950000
    user> (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ (long s) (aget xa i))) (long s))))
    "Elapsed time: 970.17736 msecs"
    4999950000
    

    in this case a few runs dropped it down to 1/3 the original time (though reflection is still the main problem here)

    if I warm them both up with dotimes the results improve a lot:

    (dotimes [_ 1000]  (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ s (nth x  i))) s))))
    "Elapsed time: 3.704714 msecs"
    
    (dotimes [_ 1000] (time  (loop [i 0 s 0] (if (< i 100000) (recur (inc i) (+ (long s) (aget xa i))) (long s)))))
    "Elapsed time: 936.03987 msecs"
    

提交回复
热议问题