Tôi đã triển khai một số số học phức tạp cơ bản trong Clojure và nhận thấy rằng nó chậm hơn khoảng 10 lần so với mã Java tương đương, ngay cả với các gợi ý kiểu.Số học phức tạp nhanh ở Clojure
Hãy so sánh:
(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]))))
đầu ra:
"Elapsed time: 69.429796 msecs"
"Elapsed time: 72.232479 msecs"
với:
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] };
}
đầu ra:
Time taken: 6 millis
Time taken: 6 millis
Trên thực tế, các gợi ý kiểu dường như không tạo ra sự khác biệt: nếu tôi loại bỏ chúng, tôi nhận được kết quả tương tự. Điều thực sự kỳ lạ là nếu tôi chạy kịch bản Clojure mà không một REPL, tôi nhận được kết quả chậm hơn:
"Elapsed time: 137.337782 msecs"
"Elapsed time: 214.213993 msecs"
Vì vậy, câu hỏi của tôi là: làm thế nào tôi có thể nhận được gần đến việc thực hiện các mã Java? Và tại sao Earth lại làm các biểu thức mất nhiều thời gian hơn để đánh giá khi chạy clojure mà không có REPL?
CẬP NHẬT ==============
Tuyệt vời, sử dụng deftype
với kiểu gợi ý trong deftype
và trong defn
s, và sử dụng dotimes
hơn repeatedly
cho hiệu suất tốt như hoặc tốt hơn phiên bản Java. Cám ơn hai bạn.
(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))))
Output:
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"
Các bạn đã cố gắng thiết lập [ '* cảnh báo-on-phản ánh *'] (http://clojuredocs.org/clojure_core /clojure.core/*warn-on-reflection*) để xem liệu có bất kỳ sự phản chiếu nào lén lút vào không? – DaoWen
@DaoWen: không, tôi chưa bao giờ sử dụng cài đặt đó. Tôi vừa chạy kịch bản lệnh một lần nữa với '(set! * Warn-on-reflection * true)' ở trên cùng của nó, và không có cảnh báo nào được in để stdout, vậy có nghĩa là không có sự phản chiếu nào được sử dụng, đúng không? Chỉ muốn chắc chắn rằng tôi đang sử dụng nó một cách chính xác. – OpenSauce