2012-03-26 28 views
5

Tôi có một câu hỏi về lý do tại sao có sự khác biệt về tốc độ giữa phương pháp vòng lặp và phương pháp lặp trong clojure. Tôi làm theo các hướng dẫn trong http://www.learningclojure.com/2010/02/clojure-dojo-2-what-about-numbers-that.html và định nghĩa hai phương pháp vuông gốc bằng cách sử dụng phương pháp Heron:Tại sao có sự khác biệt về tốc độ giữa vòng lặp của Clojure và phương pháp lặp lại

(defn avg [& nums] (/ (apply + nums) (count nums))) 
(defn abs [x] (if (< x 0) (- x) x)) 
(defn close [a b] (-> a (- b) abs (< 1e-10))) 

(defn sqrt [num] 
    (loop [guess 1] 
    (if (close num (* guess guess)) 
     guess 
    (recur (avg guess (/ num guess))) 
))) 

(time (dotimes [n 10000] (sqrt 10))) ;;"Elapsed time: 1169.502 msecs" 


;; Calculation using the iterate method 
(defn sqrt2 [number] 
    (first (filter #(close number (* % %)) 
     (iterate #(avg % (/ number %)) 1.0)))) 

(time (dotimes [n 10000] (sqrt2 10))) ;;"Elapsed time: 184.119 msecs" 

Có khoảng một sự gia tăng về tốc độ x10 giữa hai phương pháp và tôi đang tự hỏi điều gì đang xảy ra bên dưới bề mặt để nguyên nhân hai người được như vậy pronouced?

+0

tôi giả định 'sqrt2' đầu tiên là lỗi đánh máy? Ngoài ra, bạn cần phải lặp lại timings nhiều lần để có được kết quả hữu ích (jvm mất thời gian để tối ưu hóa). nó không thay đổi thực tế là một trong những chậm hơn, nhưng nó thay đổi đáng kể số lượng. –

+0

Yep ... đã sửa nó ngay bây giờ ... vậy 10000 lần là chưa đủ? – zcaudate

+0

tôi không biết chắc chắn, nhưng nó có vẻ là dựa trên thời gian - như, nó được nhanh hơn trong hai giây đầu tiên hoặc lâu hơn. bạn đã thử lặp lại nó và nhìn vào đầu ra từ thời gian()? –

Trả lời

5

Kết quả của bạn thật đáng ngạc nhiên: thông thường loop/recur là cấu trúc nhanh nhất trong Clojure để lặp.

Tôi nghi ngờ rằng JVM JIT đã làm việc ra một tối ưu hóa thông minh cho phương pháp lặp lại, nhưng không cho phiên bản lặp lại/lặp lại. Thật đáng ngạc nhiên khi điều này xảy ra thường xuyên khi bạn sử dụng mã chức năng sạch trong Clojure: nó có vẻ rất tuân theo tối ưu hóa.

Lưu ý rằng bạn có thể có được một sự tăng tốc đáng kể trong cả hai phiên bản bằng cách sử dụng một cách rõ ràng đôi:

(set! *unchecked-math* true) 

(defn sqrt [num] 
    (loop [guess (double 1)] 
    (if (close num (* guess guess)) 
     guess 
     (recur (double (avg guess (/ num guess))))))) 

(time (dotimes [n 10000] (sqrt 10))) 
=> "Elapsed time: 25.347291 msecs" 


(defn sqrt2 [number] 
    (let [number (double number)] 
    (first (filter #(close number (* % %)) 
     (iterate #(avg % (/ number %)) 1.0))))) 

(time (dotimes [n 10000] (sqrt 10))) 
=> "Elapsed time: 32.939526 msecs" 

Đúng như dự đoán, phiên bản loop/tái phát hiện có một lợi thế cạnh nhẹ. Kết quả cho Clojure 1.3

+0

Chắc chắn tăng tốc độ lặp lại/chức năng tái diễn ... cờ * bỏ chọn-toán * làm gì? – zcaudate

+0

nó sẽ tắt kiểm tra tràn cho các hoạt động nguyên thủy. hữu ích cho một số lượng nhỏ của tốc độ thêm, nhược điểm là bạn sẽ không nhận được một ngoại lệ nếu một tràn xảy ra, do đó bạn cần phải cẩn thận hơn một chút. – mikera

+0

Tuyệt vời! nó chắc chắn tăng tốc độ lặp lại/chức năng recur ... tôi đã nhận 38.0ms cho sqrt và 67.3ms sqrt2 ... =) – zcaudate

Các vấn đề liên quan