Stavo implementando alcuni aritmetici di numeri complessi di base in Clojure e ho notato che era circa 10 volte più lento di un codice Java approssimativamente equivalente, anche con suggerimenti tipo.Aritmetica di numeri complessi veloci in Clojure
Confronta:
(defn plus [[^double x1 ^double y1] [^double x2 ^double y2]]
[(+ x1 x2) (+ y1 y2)])
(defn times [[^double x1 ^double y1] [^double x2 ^double y2]]
[(- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2))])
(time (dorun (repeatedly 100000 #(plus [1 0] [0 1]))))
(time (dorun (repeatedly 100000 #(times [1 0] [0 1]))))
uscita:
"Elapsed time: 69.429796 msecs"
"Elapsed time: 72.232479 msecs"
con:
public static void main(String[] args) {
double[] z1 = new double[] { 1, 0 };
double[] z2 = new double[] { 0, 1 };
double[] z3 = null;
long l_StartTimeMillis = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
z3 = plus(z1, z2); // assign result to dummy var to stop compiler from optimising the loop away
}
long l_EndTimeMillis = System.currentTimeMillis();
long l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis;
System.out.format("Time taken: %d millis\n", l_TimeTakenMillis);
l_StartTimeMillis = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
z3 = times(z1, z2);
}
l_EndTimeMillis = System.currentTimeMillis();
l_TimeTakenMillis = l_EndTimeMillis - l_StartTimeMillis;
System.out.format("Time taken: %d millis\n", l_TimeTakenMillis);
doNothing(z3);
}
private static void doNothing(double[] z) {
}
public static double[] plus (double[] z1, double[] z2) {
return new double[] { z1[0] + z2[0], z1[1] + z2[1] };
}
public static double[] times (double[] z1, double[] z2) {
return new double[] { z1[0]*z2[0] - z1[1]*z2[1], z1[0]*z2[1] + z1[1]*z2[0] };
}
uscita:
Time taken: 6 millis
Time taken: 6 millis
In effetti, i suggerimenti tipo non sembrano fare la differenza: se li rimuovo ottengo circa lo stesso risultato. La cosa veramente strana è che se faccio funzionare lo scritto Clojure senza un REPL, ottengo risultati più lenti:
"Elapsed time: 137.337782 msecs"
"Elapsed time: 214.213993 msecs"
Quindi le mie domande sono: come posso ottenere vicino alle prestazioni del codice Java? E perché sulla Terra le espressioni impiegano più tempo per valutare quando si esegue il clojure senza un REPL?
UPDATE ==============
Grande, utilizzando deftype
con tipo suggerimenti nel deftype
e nelle defn
s, e l'utilizzo di dotimes
piuttosto che repeatedly
offre prestazioni buone come o meglio della versione Java. Grazie a tutti e due.
(deftype complex [^double real ^double imag])
(defn plus [^complex z1 ^complex z2]
(let [x1 (double (.real z1))
y1 (double (.imag z1))
x2 (double (.real z2))
y2 (double (.imag z2))]
(complex. (+ x1 x2) (+ y1 y2))))
(defn times [^complex z1 ^complex z2]
(let [x1 (double (.real z1))
y1 (double (.imag z1))
x2 (double (.real z2))
y2 (double (.imag z2))]
(complex. (- (* x1 x2) (* y1 y2)) (+ (* x1 y2) (* y1 x2)))))
(println "Warm up")
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(println "Try with dorun")
(time (dorun (repeatedly 100000 #(plus (complex. 1 0) (complex. 0 1)))))
(time (dorun (repeatedly 100000 #(times (complex. 1 0) (complex. 0 1)))))
(println "Try with dotimes")
(time (dotimes [_ 100000]
(plus (complex. 1 0) (complex. 0 1))))
(time (dotimes [_ 100000]
(times (complex. 1 0) (complex. 0 1))))
uscita:
Warm up
"Elapsed time: 92.805664 msecs"
"Elapsed time: 164.929421 msecs"
"Elapsed time: 23.799012 msecs"
"Elapsed time: 32.841624 msecs"
"Elapsed time: 20.886101 msecs"
"Elapsed time: 18.872783 msecs"
Try with dorun
"Elapsed time: 19.238403 msecs"
"Elapsed time: 17.856938 msecs"
Try with dotimes
"Elapsed time: 5.165658 msecs"
"Elapsed time: 5.209027 msecs"
Hai provato l'impostazione [ '* mettere in guardia-on-riflessione *'] (http://clojuredocs.org/clojure_core /clojure.core/*warn-on-reflection*) per vedere se ci sono riflessi in cui entrare di nascosto? – DaoWen
@DaoDao: no, non ho mai usato quell'impostazione. Ho appena eseguito nuovamente lo script con '(set! * Warn-on-reflection * true)' in cima ad esso, e non ci sono avvisi stampati sullo stdout, quindi significa che non viene utilizzato alcun riflesso, giusto? Voglio solo assicurarmi che lo stia usando correttamente. – OpenSauce