What *warn-on-reflection* doesn’t tell you about arrays
user=> (time (let [a (make-array Object 100)] (dotimes [i 10000000] (aset a (rem i 100) nil)))) Reflection warning, NO_SOURCE_PATH:840 - call to aset can't be resolved. "Elapsed time: 136063.015272 msecs"
With aget/aset, the index must be hinted to be an int:
user=> (time (let [a (make-array Object 100)] (dotimes [i 10000000] (aset a (int (rem i 100)) nil)))) "Elapsed time: 1064.546402 msecs"
Wow, more than 100x faster (reflection is bad) but despite the compiler doesn’t complain one can help it to choose a faster path:
user=> (time (let [a #^"[Ljava.lang.Object;" (make-array Object 100)] (dotimes [i 10000000] (aset a (int (rem i 100)) nil)))) "Elapsed time: 247.446882 msecs"
On the whole we get a 500x speed-up with only two type hints.
Very interesting!
Sadly this trick doesn’t seem to work for arrays of primitives, or my syntax is wrong?
(let [a (make-array Double/TYPE 10000)
b #^doubles (make-array Double/TYPE 10000)]
(time (doseq [i (range 100), j (range 100)] (aget a (int (+ i (* j 100))))))
(time (doseq [i (range 100), j (range 100)] (aget b (int (+ i (* j 100)))))))
Also… is there any way to get (aget a i j) to be fast – or do I have to go with a flat array and calc the offsets?
Btw looks like a small bit of markup leaked into your code on the new site:
“…”
It doesn’t appear on the old site.
opps the comment ate the tags,
they are ins tags that are leaking through.
Hmmm actually I do see a speed-up for larger samples:
(let [a (make-array Double/TYPE 1000000)
b #^doubles (make-array Double/TYPE 1000000)]
(time (doseq [i (range 1000), j (range 1000)] (aget a (int (+ i (* j 100))))))
(time (doseq [i (range 1000), j (range 1000)] (aget b (int (+ i (* j 100)))))))
“Elapsed time: 1452.764384 msecs”
“Elapsed time: 1118.704557 msecs”
@Timothy, I think your tests are dominated by boxed arithmetic.
(let [a #^doubles (make-array Double/TYPE 100)]
(time (dotimes [i 100000000] (aget a (int (rem i 100))))))
“Elapsed time: 1806.106502 msecs”
user=> (let [a (make-array Double/TYPE 100)]
(time (dotimes [i 100000000] (aget a (int (rem i 100))))))
“Elapsed time: 17794.809466 msecs”
Note that dotimes automatically hints ‘i as an int.
I measure this difference even for smaller arrays:
user=> (let [a #^doubles (make-array Double/TYPE 100)]
(time (dotimes [i 100000] (aget a (int (rem i 100))))))
“Elapsed time: 14.871513 msecs”
user=> (let [a (make-array Double/TYPE 100)]
(time (dotimes [i 100000] (aget a (int (rem i 100))))))
“Elapsed time: 30.586988 msecs”
When I hint your code to perform primitive arithmetic, I get similar improvements:
user=> (let [a (make-array Double/TYPE 1000000)
b #^doubles (make-array Double/TYPE 1000000)]
(time (doseq [i (range 1000), j (range 1000)] (aget a (int (+ (int i) (* (int j) 100))))))
(time (doseq [i (range 1000), j (range 1000)] (aget b (int (+ (int i) (* (int j) 100)))))))
“Elapsed time: 415.131113 msecs”
“Elapsed time: 209.553309 msecs”
Ah, thanks!
This very blog is no doubt educating and also informative. I have chosen a lot of helpful tips out of this source. I ad love to go back again soon. Thanks a bunch!
69VN là nhà cái cá cược trực tuyến hàng đầu tại Việt Nam, mang đến trải nghiệm giải trí đẳng cấp với hơn 500 trò chơi đa dạng như cá cược thể thao, casino trực tuyến, bắn cá, nổ hũ và game bài. 69vnpro.net