【问题标题】:Clojure: gc overhead limit exceeded, lazy evaluation, pi sequenceClojure:超出 gc 开销限制、延迟评估、pi 序列
【发布时间】:2010-10-17 19:48:11
【问题描述】:

对于下一个代码:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (Math/pow -1 n) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (iterate inc 0)))

(println (first ((avg-damp-n 10) (sums Gregory-Leibniz-pi))))

对于 n=20,我收到“超出 gc 开销限制”错误。 我该如何解决这个问题?

更新:我更改了 avg-damp-n 函数

(defn avg-damp-n
  [n seq]
  (if (= n 0) seq
      (recur (dec n) (avg-damp seq))))

现在我可以得到 n=20 的数字

(time
 (let [n 20]
   (println n (first (avg-damp-n n (sums Gregory-Leibniz-pi))))))

20 3.141593197943081
"Elapsed time: 3705.821263 msecs"

更新 2 我修复了一些错误,现在它工作得很好:

(ns clojure101.series)

(defn avg [[x y]] (/ (+ x y) 2))

(defn avg-damp
  [seq]
  (map avg (partition 2 1 seq)))

(defn avg-damp-n
  [n]
  (apply comp (repeat n avg-damp)))

(defn sums
  [seq]
  (reductions + seq))

(defn Gregory-Leibniz-n
  [n]
  (/ (int (Math/pow -1 n)) (inc (* 2 n))))

(def Gregory-Leibniz-pi
     (map #(* 4 (Gregory-Leibniz-n %)) (range)))

; π = 3.14159265358979323846264338327950288419716939937510...

(time
 (let [n 100]
   (println n (double (first ((avg-damp-n n) (sums Gregory-Leibniz-pi)))))))

输出:

100 3.141592653589793
"Elapsed time: 239.253227 msecs"

【问题讨论】:

    标签: clojure garbage-collection lisp jvm lazy-evaluation


    【解决方案1】:

    正如 kotarak 所说,在惰性 seq 上堆叠惰性 seq 似乎效率很低 关于GC。我可以在慢原子系统上重现这个问题。另见:

    Error java.lang.OutOfMemoryError: GC overhead limit exceeded

    对我来说,Gregory-Leibniz PI 计算直接转换为仅使用一个惰性序列的 Clojure 代码:

    (defn Gregory-Leibniz-pi [n]
      (->> (range n)
           (map (fn [n] (/ (Math/pow -1 n) (inc (* 2 n)))))
           (apply +)
           (* 4)))
    

    【讨论】:

      【解决方案2】:

      嗯...这对我有用。在 Windows XP 上使用 Clojure 1.2 进行测试。

      user=> (defn avg
               [xs & {:keys [n] :or {n 2}}]
               (/ (reduce + xs) n))
      #'user/avg
      user=> (defn Gregory-Leibniz-n
               [n]
               (/ (Math/pow -1 n) (inc (+ n n))))
      #'user/Gregory-Leibniz-n
      user=> (->> (range)
               (map #(* 4 (Gregory-Leibniz-n %)))
               (reductions +)
               (partition 20)
               (map #(avg % :n 20))
               first
               println)
      3.1689144018345354
      

      这是正确的答案吗?我不知道这个 Gregory-Leibniz 递归,所以我 不确定这是否正确。

      我注意到了一点:你太聪明了。即你的 avg-damp-n 在惰性序列上堆叠惰性序列。由于您可以插入n 的任意值, 您迟早会遇到大型n 的堆栈溢出 这样的场景。如果有一个直接的解决方案,您应该更喜欢 它。不过,我不确定这是您的实际问题。 (正如我所说:宁可 它对我有用。)

      【讨论】:

      • 考虑到这应该解决 Pi,我认为这不是正确的答案。 ;)
      • @ataggert 你说得对。 ;)(小编辑:但它可能仍然是 OP 期望的数字......)(当然,随着 (partition 2 1 ...) 的更改,上面的更改不再起作用。)
      【解决方案3】:

      首先,尝试可行的愚蠢解决方案:增加您的 java 堆空间。

      ;in your clojure launch script
      java -Xmx2G ...other options...
      

      程序的一部分在分区中不是惰性的,但是将其更改为惰性(通过摆脱对count 的调用)仍然会给我一个默认堆大小的 OutOfMemoryError。用减少计算的平均值替换 avg-damp-n 的巧妙之处

      (take (integer-exponent 2 20) seq)
      

      仍然会导致 OutOfMemoryError。查看其他所有内容的来源,我看不到任何其他看起来应该消耗堆的东西。

      【讨论】:

        猜你喜欢
        • 2011-05-21
        • 1970-01-01
        • 2017-12-27
        • 2013-07-13
        • 2018-03-29
        • 2012-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多