Closed zoejules closed 10 years ago
Hello,
You are really kind of comparing apples and oranges, and primarily because your Clojure implementation uses immutable sequences, while the one in Java - mutable arrays. Immutable collections will always be slower, and I'm pretty sure if you are developing a game, you want to use arrays.
Clojure has a basic support for arrays, although the code with them becomes totally disgusting. But it can be improved with a few tailored macros. Here's a terrible example, but I've managed to cut execution time to ~0.05 msec on my Nexus 7:
(let [^"[[Ljava.lang.Object;" mat (to-array-2d (repeat 3 (range 1 4)))]
(let [n (int (count mat))
m (int (count (aget mat 0)))
t (make-array Long/TYPE m n)]
(loop [i 0 j 0]
(if (>= i n)
t
(if (>= j m)
(recur (inc i) 0)
(do
(aset ^"[J" (aget ^"[[J" t j) i
(long (aget ^"[Ljava.lang.Object;" (aget mat i) j)))
(recur i (inc j))))))
t))
But if I were you, and still wanted to use Clojure for developing an Android game, I would write the most performance-critical code on the Java side. As you can see, working with arrays is actually much easier in Java.
Thank you for your detailed answer and taking the time with the code example, I see that it's complicated though. We really considered abandoning clojure in favor of java at some (many) point. We don't want to lose the REPL and we've also developed a small application which monitors files for change and sends it directly to the device (if you are interested I can send it to you with source code). So now the hybrid solution what you've proposed seems best. (It also worth mentioning that we tried integrating Light Table with lein-droid but it has it's own nrepl middleware and we decided it didn't worth the effort at that point)
BTW We also tried transients but they didn't help much. The following (admittedly stupid) code takes 0.1 msec
(defn transpose2 [mat]
(let [transient-mat (transient mat)]
(persistent! (assoc! transient-mat 1 (transient-mat 3)
3 (transient-mat 1)
2 (transient-mat 6)
6 (transient-mat 2)
5 (transient-mat 7)
7 (transient-mat 5)))
)
)
But I see now that we have to go with arrays anyway. Sorry for the rambling I'm super tired.
No problem. I wish I could help more.
Speaking of transients, they don't really shine on small collections, because transients themselves require pretty complex bookkeeping. If the collections are small, it eats away the benefit. An excellent book The Joy of Clojure contains a separate chapter about performance optimizations, I recommend reading it before trying to boost performance in Clojure.
And about the tool you mentioned, sure, if you open sourced it, then I would love to take a look.
Thank you for your help! I guess you can close this case. As for the tool I mentioned I would be too embarassed to publish it it's very clumpsy... :) But if you are interested I will send it to you in a zip file or share it in google docs. BTW it's in c#. It's only value is to see some code examples how to communicate with an NREPL server through a socket in .Net.
Anyway is there a better place to ask public questions? I still have one question: what is your workflow when you use lein-droid? Do you run a "lein droid doall" and then use the command line repl to update defn's etc. or you have some editor with integrated repl support?
Step by step:
lein droid forward-port
is executed (doall
does it as well), you can connect to localhost on port 9999 (by default, can be changed in project.clj
and there is your REPL).
Firstly I apologize creating this issue I couldn't find a better place for this. I suspect it is an issue but It's easily possible that I messed up something (being a beginner in clojure).
So basically the following code takes about 0.2 msecs on a nexus 4:
(apply vector (apply map vector [[1 2 3][1 2 3][1 2 3]])) ;we want to transpose a matrix
In a normal (desktop) leiningen project the same codes runs literally 100 times faster (0.002 msec).
I can hardly believe that a 1 Ghz ARM processor is 100 times slower than a 3.3 Ghz Core-i5 desktop processor. I know it's apples and oranges but still it's very surprising.
Maybe there is a better way of transposing a matrix but the thing is we measured slow times with other functions too.
We are currently developing a game and we have to do every frame within about 33 msecs to have 30 FPS.
So I guess the execution time of the above code should be one magnitude faster in order to be usable in real-time applications.
EDIT:
We've tried a similar algorithm with java:
public static double[][] transposeMatrix(double [][] m){ double[][] temp = new double[m[0].length][m.length]; for (int i = 0; i < m.length; i++) for (int j = 0; j < m[0].length; j++) temp[j][i] = m[i][j]; return temp; }
with this input: new double[][]{{1,2,3},{4,5,6},{7,8,9}}
this took about 0.02 so it was 10 times faster but the reason for this might be the difference between the too implementations.
So now we're officially confused.