Trước khi thí nghiệm tôi đã thêm dòng tiếp theo trong project.clj:
:jvm-opts ^:replace [] ; Makes measurements more accurate
đo cơ bản:
(def a (double-array (range 1000000))) ; 10 is too small for performance measurements
(quick-bench (sum-of-squares a)) ; ... Execution time mean : 27.617748 ms ...
(quick-bench (sum-of-squares2 a)) ; ... Execution time mean : 1.259175 ms ...
Điều này ít nhiều phù hợp với chênh lệch thời gian trong câu hỏi. Hãy cố gắng không sử dụng các mảng Java (không thực sự là thành ngữ cho Clojure):
(def b (mapv (partial * 1.0) (range 1000000))) ; Persistent vector
(quick-bench (sum-of-squares b)) ; ... Execution time mean : 14.808644 ms ...
Gần gấp 2 lần nhanh hơn. Bây giờ, hãy xóa các gợi ý loại:
(defn sum-of-squares3
"Given a vector v, compute the sum of the squares of elements."
[v]
(r/fold + (r/map #(* % %) v)))
(quick-bench (sum-of-squares3 a)) ; Execution time mean : 30.392206 ms
(quick-bench (sum-of-squares3 b)) ; Execution time mean : 15.583379 ms
Thời gian thực hiện chỉ tăng nhẹ so với phiên bản có gợi ý loại. Bằng cách này, phiên bản với transducers có hiệu suất rất giống nhau và có nhiều bụi:
(defn sum-of-squares3 [v]
(transduce (map #(* % %)) + v))
Bây giờ về thêm kiểu gián tiếp. Chúng tôi thực sự có thể tối ưu hóa sum-of-squares
thực hiện đầu tiên:
(defn square ^double [^double x] (* x x))
(defn sum-of-squares4
"Given a vector v, compute the sum of the squares of elements."
[v]
(r/fold + (r/map square v)))
(quick-bench (sum-of-squares4 b)) ; ... Execution time mean : 12.891831 ms ...
(defn pl
(^double [] 0.0)
(^double [^double x] (+ x))
(^double [^double x ^double y] (+ x y)))
(defn sum-of-squares5
"Given a vector v, compute the sum of the squares of elements."
[v]
(r/fold pl (r/map square v)))
(quick-bench (sum-of-squares5 b)) ; ... Execution time mean : 9.441748 ms ...
Lưu ý # 1: Loại gợi ý về lý luận và trả lại giá trị của sum-of-squares4
và sum-of-squares5
không có lợi ích hiệu suất bổ sung.
Lưu ý # 2: Thường là thực tiễn không tốt để bắt đầu với optimizations. Phiên bản thẳng về phía trước (apply + (map square v))
sẽ có hiệu suất đủ tốt cho hầu hết các trường hợp. sum-of-squares2
là rất xa thành ngữ và sử dụng nghĩa đen không có khái niệm Clojure. Nếu điều này thực sự là mã quan trọng hiệu suất - tốt hơn để thực hiện nó trong Java và sử dụng interop. Mã sẽ sạch hơn mặc dù có 2 ngôn ngữ. Hoặc thậm chí thực hiện nó trong mã không được quản lý (C, C++) và sử dụng JNI (không thực sự bảo trì nhưng nếu được triển khai đúng cách, có thể cung cấp hiệu suất tốt nhất có thể).
Cảm ơn Rodrigo. Tôi không biết về areduce. Đó là chính xác những gì tôi cần, một cách để nói với một giảm để sử dụng aget ... – Scott
Vui mừng được giúp đỡ, Scott! –