2013-10-06 2 views
0

Я начал решать project euler проблемы в clojure как средство изучения языка. при внедрении сита для эратосфенов для проблемы 10 i изначально испытывала крайне низкую производительность, которая, по-видимому, вытекает из прохождения boolean-array. я нашел, что могу обойти проблему, добавив подсказки типа или переупорядочивая код, чтобы вспомогательные функции могли получить доступ к boolean-array непосредственно из родительской области, но я не понимаю, почему это необходимо. (type sieve) в любой из вспомогательных функций возвращает [Z, поэтому кажется, что clojure уже знает, что это boolean-array. может ли кто-нибудь объяснить, почему здесь нужны подсказки типа?Плохая производительность при прохождении вокруг булевых массивов в clojure

извинения за стену кода, я не знаю, какие части можно удалить, пока еще иллюстрирует проблему.

;;; returns a vector containing all primes smaller than limit 
(defn gen-primes-orig [limit] 
    (defn next-unmarked [sieve v] 
    (loop [i (inc v)] 
     (cond 
     (>= i limit) nil 
     (false? (aget sieve i)) i 
     :true (recur (inc i))))) 

    (defn mark-powers-of! [sieve v] 
    (loop [i (+ v v)] 
     (if (>= i limit) sieve 
      (do 
      (aset-boolean sieve i true) 
      (recur (+ i v)))))) 

    (defn collect-primes [sieve] 
    (loop [primes [] 
      i 0] 
     (cond 
     (>= i limit) primes 
     (false? (aget sieve i)) (recur (conj primes i) (inc i)) 
     :true (recur primes (inc i))))) 

    (let [sieve (boolean-array limit false)] 
    ;; 0 and 1 are not primes 
    (aset-boolean sieve 0 true) 
    (aset-boolean sieve 1 true) 

    (loop [v 0] 
     (let [v (next-unmarked sieve v)] 
     (if (nil? v) (collect-primes sieve) 
      (do 
       (mark-powers-of! sieve v) 
       (recur v))))))) 

(defn gen-primes-hint [limit] 
    (defn next-unmarked [^booleans sieve v] 
    ;; same body as in gen-primes-orig 
    ) 
    (defn mark-powers-of! [^booleans sieve v] 
    ;; same body as in gen-primes-orig 
    ) 
    (defn collect-primes [^booleans sieve] 
    ;; same body as in gen-primes-orig 
    ) 
    (let [sieve (boolean-array limit false)] 
    ;; same body as in gen-primes-orig 
    )) 

(defn gen-primes-letfn [limit] 
    (let [sieve (boolean-array limit false)] 
    ;; 0 and 1 are not primes 
    (aset-boolean sieve 0 true) 
    (aset-boolean sieve 1 true) 

    (letfn [(next-unmarked [v] 
       ;; same body as in gen-primes-orig 
      ) 
      (mark-powers-of! [v] 
       ;; same body as in gen-primes-orig 
      ) 
      (collect-primes [] 
       ;; same body as in gen-primes-orig 
      )] 
     (loop [v 0] 
     (let [v (next-unmarked v)] 
      (if (nil? v) (collect-primes) 
       (do 
       (mark-powers-of! v) 
       (recur v)))))))) 

и вот время работы для каждой из трех смесей. (Результат был удален. Все смеси дают одинаковый, правильный результат.)

user> (time (apply + (gen-primes-orig 2000000))) 
"Elapsed time: 108001.047327 msecs" 
user> (time (apply + (gen-primes-hint 2000000))) 
"Elapsed time: 2599.091978 msecs" 
user> (time (apply + (gen-primes-letfn 2000000))) 
"Elapsed time: 2768.060355 msecs" 

ответ

0

type функция использует метод .getClass на переданном объекте, чтобы вернуть класс объекта. то есть он использует отражение во время выполнения для определения класса объекта. Где, как подсказки типа, помогает компилятору clojure генерировать эффективный код, избегая генерации кода на основе отражения. В основном type не имеет ничего общего с типом подсказок, который помогает компилятору clojure испускать эффективный код.

+0

ОК, спасибо. Я думал, что clojure будет использовать вывод типа для определения правильного типа вызовов. и для этого конкретного случая не следует ли определять тип во время компиляции, так как функции являются «внутренними», и, следовательно, все точки вызова известны? – MrGnu

Смежные вопросы