As far as I can tell there is currently no way to hint 2d array access
fast
(def a (make-array Double/TYPE 100 100))
(time (doseq [i (range 100), j (range 100)] (aget a (int i) (int j))))
"Elapsed time: 836.800335 msecs"
I can't find any combination of type hinting to speed it up.
However with a trivial change to core.clj [just added (int idx) in the
multi-arg case]:
> (apply aget (aget array (int idx)) idxs)))
(time (doseq [i (range 100), j (range 100)] (aget a i j)))
"Elapsed time: 40.162875 msecs"
It is fast! Yay!
That's great, almost as fast as a 1d array:
(def b (make-array Double/TYPE 10000))
(time (doseq [i (range 100), j (range 100)] (aget b (int (+ i (* j
100))))))
"Elapsed time: 27.701047 msecs"
But still it seems for maximum speed you would go with a 1d array.
Now for 1d arrays, the index also cannot be anything other than an
integer, so we could do the same thing [just added (int ~i)]:
{:inline (fn [a i] `(. clojure.lang.RT (aget ~a (int ~i))))
(time (doseq [i (range 100), j (range 100)] (aget b (+ i (* j 100)))))
"Elapsed time: 27.701047 msecs"
Great, it is just as fast as when we had to coerce to int.
But the coercion comes at a small cost when we don't need it:
(time (amap b idx ret (aset ret idx (inc (aget b idx)))))
original: "Elapsed time: 23.06673 msecs"
modified: "Elapsed time: 27.457263 msecs" <-- slower :( :( :(
So for the 1d case for maximum speed it appears better to leave it to
the user to coerce when necessary. The index must be an integer - is
there any way to have this fast in both cases? If not another
alternative would be to have an aget* and aget be a macro that
coerces, to allow both forms.
For multi-dimensional it appears the coercion must (and should) happen
in core?
Then I came across this post by cgrand:
http://clj-me.cgrand.net/2009/08/06/what-warn-on-reflection-doesnt-tell-you-about-arrays/
Indicates that type-hinting the array itself can provide a significant
speed-up, but I don't understand why, is this just more reflection or
is something else going on here?
In theory it should be possible to create a macro that creates
automatically type-hinted arrays (seeing the type is passed in) the
trick being to convert the type to a hint string. How would I do that?
And finally, it is actually faster to have an array of Objects and
store doubles in them than to use an array of doubles (due to boxing I
think). In my experimentation about 10% faster. Presumably an array of
doubles would be far less memory however.
(time (let [a #^"[Ljava.lang.Object;" (make-array Object 100)]
(dotimes [i 10000000] (aget a (int (rem i 100))))))
"Elapsed time: 3825.546843 msecs"
(time (let [a #^doubles (make-array Double/TYPE 100)] (dotimes [i
10000000] (aget a (int (rem i 100))))))
"Elapsed time: 4246.330734 msecs"
I know micro-benchmarks can be misleading, thanks for reading - please
point out any errors I've made.
Regards,
Tim.