2013-05-14 35 views
8

Tôi có một chương trình ClojureScript chủ yếu thực hiện các phép tính toán trên các bộ sưu tập. Nó được phát triển trong Clojure độc ​​lập, độc lập, vì vậy rất dễ dàng để chuẩn bị nó. Trước sự ngạc nhiên của tôi (và trái với những gì các câu trả lời sẽ gợi ý cho Which is faster, Clojure or ClojureScript (and why)?), cùng một mã trong ClojureScript chạy chậm hơn 5-10 lần so với tương đương Clojure của nó.Cải thiện hiệu suất của chương trình ClojureScript

Đây là những gì tôi đã làm. Tôi đã mở lein repl và trình duyệt được phát lại tại http://clojurescript.net/. Sau đó, tôi đã thử các đoạn mã này trong cả hai REPL.

(time (dotimes [x 1000000] (+ 2 8))) 

(let [coll (list 1 2 3)] (time (dotimes [x 1000000] (first coll)))) 

Sau đó, tôi mở một giao diện điều khiển javascript tại repl trình duyệt và viết một hàm chuẩn tối giản,

function benchmark(count, fun) { 
    var t0 = new Date(); 
    for (i = 0; i < count; i++) { 
    fun(); 
    } 
    var t1 = new Date(); 
    return t1.getTime() - t0.getTime(); 
} 

Quay lại trình duyệt REPL:

(defn multiply [] (* 42 1.2)) 

Sau đó thử cả javascript mẹ đẻ phép nhân và biến thể clojurescript của nó trong bảng điều khiển javascript,

benchmark(1000000, cljs.user.multiply); 

benchmark(1000000, function(){ 42 * 1.2 }); 

Những gì tôi thấy

  • Native toán javascript được so sánh với toán học trong clojure
  • ClojureScript là chậm hơn 5-10 lần so với một trong hai chúng

Bây giờ câu hỏi của tôi là, làm thế nào thế nào tôi có thể cải thiện hiệu suất của chương trình ClojureScript của tôi?

Có một số cách tiếp cận tôi đã xem xét cho đến nay

  • Mùa thu trở lại sử dụng các mảng javascript có thể thay đổi và các đối tượng đằng sau hậu trường. (Điều này có thể được không?)
  • Quay lại sử dụng toán tử toán học javascript gốc. (Đây có phải là có thể ở tất cả?)
  • Sử dụng javascript mảng một cách rõ ràng với (aget js/v 0)
  • Sử dụng thực hiện ít tham vọng của clojure-cho-javascript, như https://github.com/chlorinejs/chlorine hoặc https://github.com/gozala/wisp Họ tạo ra một javascript nhiều thành ngữ, nhưng họ không hỗ trợ namespace mà tôi đang sử dụng rất nhiều.

Trả lời

10

JavaScript có lợi nhuận rõ ràng, vì vậy

function() { 42 * 1.2 } 

không có gì; thay vào đó, bạn sẽ cần phải đánh dấu chuẩn là

function() { return 42 * 1.2 } 

. Điều này xảy ra chính xác những gì phiên bản ClojureScript biên dịch, vì vậy sẽ không có bất kỳ sự khác biệt nào (trong ClojureScript, các hàm số học cơ bản trong việc sử dụng không cao hơn được inline như các biểu thức JavaScript dựa trên toán tử).

Bây giờ, Clojure chắc chắn nhanh hơn ClojureScript tại thời điểm này. Một phần lý do là Clojure vẫn được điều chỉnh cẩn thận hơn ClojureScript, mặc dù ClojureScript đang cải thiện với tốc độ khá lớn trong bộ phận này.Một phần khác là Clojure có một JIT trưởng thành hơn để tận dụng lợi thế (các động cơ JS hiện đại, V8 nói riêng, là khá lớn, nhưng chưa hoàn toàn là HotSpot).

Độ lớn của sự khác biệt hơi phức tạp để đo lường; thực tế là các JIT có liên quan có nghĩa là một vòng lặp với một cơ thể không có bất kỳ tác dụng phụ nào, chẳng hạn như một trong câu hỏi, có khả năng sẽ được tối ưu hóa, thậm chí ngay cả khi chạy đầu tiên qua nó. , được sử dụng bởi HotSpot và Tôi nghĩ rằng cũng V8 - tôi phải kiểm tra để chắc chắn). Vì vậy, tốt hơn để chuẩn cái gì đó như

(def arr (long-array 1)) 

;;; benchmark this 
(dotimes [_ 1000000] 
    (aset (longs arr) 0 (inc (aget (longs arr) 0)))) 

(longs gọi để tránh phản chiếu trong Clojure; cũng có thể sử dụng ^longs gợi ý).

Cuối cùng, chắc chắn là trường hợp, trong cả Clojure và ClojureScript, đối với một số loại mã đặc biệt hiệu suất đặc biệt, tốt nhất là sử dụng mảng gốc và như vậy. May mắn thay, không có vấn đề với làm như vậy: ở phía bên ClojureScript, bạn đã có array, js-obj, aget, aset, make-array, bạn có thể sử dụng :mutable siêu dữ liệu trên các lĩnh vực trong deftype để có thể set! chúng trong cơ thể phương pháp, vv

7

Toán ClojureScript Toán JavaScript. Có, nếu hiệu suất là quan trọng, hãy sử dụng các mảng JavaScript và các toán tử cấp thấp được cung cấp, chúng được đảm bảo tạo mã tối ưu nếu có thể (nghĩa là không sử dụng đơn hàng cao hơn). Các cấu trúc dữ liệu liên tục ClojureScript được viết theo cách này: đột biến mảng, số học, bit twiddling.

Tôi có một ví dụ nhỏ về ClojureScript hiệu quả - http://github.com/swannodette/cljs-stl/blob/master/src/cljs_stl/spectral/demo.cljs mà bạn có thể thấy hữu ích làm hướng dẫn.

+1

Tôi nghĩ bạn đã có chuẩn phổ ở ClojureScript ở đâu đó! +1 cho điều đó. –

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