为什么Clojure和#x27;s循环和迭代方法

为什么Clojure和#x27;s循环和迭代方法,clojure,loops,Clojure,Loops,我有一个问题,为什么clojure中的循环方法和迭代方法在速度上存在如此大的差异。我遵循中的教程,使用Heron方法定义了两种平方根方法: (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 [gues

我有一个问题,为什么clojure中的循环方法和迭代方法在速度上存在如此大的差异。我遵循中的教程,使用Heron方法定义了两种平方根方法:

(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"
(定义平均值[&nums](/(应用+nums)(计数nums)))
(定义abs[x](如果(a(-b)abs(<1e-10)))
(定义sqrt[数量]
(循环[猜测1]
(如果(关闭数值(*猜测))
猜测
(重复(平均猜测(/num猜测)))
)))
(时间(点时间[n 10000](sqrt 10));;“运行时间:1169.502毫秒”
;; 用迭代法计算
(定义sqrt2[编号]
(第一个(过滤器#)(关闭编号(*%%))
(迭代#(平均%(/数字%)1.0)))
(时间(点时间[n 10000](sqrt2 10));;“运行时间:184.119毫秒”

这两种方法之间的速度提高了约10倍,我想知道表面下发生了什么事情导致这两种方法如此快?

您的结果令人惊讶:通常循环/重复是Clojure中循环速度最快的构造

我怀疑jvmjit已经为iterate方法进行了巧妙的优化,但没有为循环/递归版本进行优化。令人惊讶的是,在Clojure中使用干净的函数代码时,这种情况经常发生:它似乎非常适合优化

请注意,通过显式使用double,您可以在两个版本中获得显著的加速:

(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"

正如预期的那样,循环/重现版本现在有一个轻微的边缘。结果是Clojure 1.3的

我假设first
sqrt2
是一个打字错误?此外,您需要多次重复计时以获得有用的结果(jvm需要时间进行优化)。这并没有改变一个人慢的事实,但它确实显著地改变了数字。是的。。。现在修好了。。。所以10000次还不够?我不确定,但它似乎是基于时间的——就像,它在第一秒左右会变得更快。您是否尝试重复它并查看time()的输出?10000次通常就足够了,但您需要将整个过程运行多次,因为您的第一次或两次运行将包括初始解释的成本+JIT编译开销。通常我只需重新运行
(time…
命令四到五次,直到计时稳定。肯定会加快循环/重现功能。。。unchecked math标志的作用是什么?它关闭基本操作的溢出检查。对于少量的额外速度很有用,缺点是如果发生溢出,你不会得到异常,所以你需要更加小心。太棒了!它肯定会加速循环/重现功能。。。我得到了38.0ms的sqrt和67.3ms的sqrt2…=)