为什么这段代码会在CMUCL中溢出堆?即使我给它400MB内存(setq extensions:*bytes-consed-between-gcs* 400000000)
CMUCL仍然会扼杀它。
; [GC threshold exceeded with 12,012,544 bytes in use. Commencing GC.]
; [GC completed with 188,064 bytes retained and 11,824,480 bytes freed.]
; [GC will next occur when at least 400,188,064 bytes are in use.]
; [GC threshold exceeded with 400,202,280 bytes in use. Commencing GC.]
; [GC completed with 207,120 bytes retained and 399,995,160 bytes freed.]
; [GC will next occur when at least 400,207,120 bytes are in use.]
这个代码在CCL和SBCL上运行正常,但我还没看过他们的内存使用情况。
这是CMUCL中的错误吗?我相信所有这些功能都是尾递归的。
(defun sqrt-iter (guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve guess x)
x)))
(defun improve (guess x)
(average guess (/ x guess)))
(defun average (x y)
(/ (+ x y) 2))
(defun good-enough? (guess x)
(< (abs (- (* guess guess) x)) 0.001))
(defun mysqrt (x)
(sqrt-iter 1.0 x))
(defun zint (x acc step)
(setq num-iter (+ 1 num-iter))
(if (>= x 10000.0)
acc
(zint (+ x step)
(+ acc (* step (mysqrt x)))
step)))
(setq num-iter 0)
(format t "result=~A; iterations=~A~%" (zint 0.0 0.0 .001) num-iter)
(quit)
修改 是的,CMUCL肯定会付出很多而且不必要的。试试这个简单的例子:
$ ~/cmucl/bin/lisp
...
* (defun foo () (bar))
FOO
* (defun bar () (foo))
BAR
* (foo)
; [GC threshold exceeded with 12,009,008 bytes in use. Commencing GC.]
; [GC completed with 111,816 bytes retained and 11,897,192 bytes freed.]
; [GC will next occur when at least 12,111,816 bytes are in use.]
; [GC threshold exceeded with 12,120,912 bytes in use. Commencing GC.]
; [GC completed with 120,016 bytes retained and 12,000,896 bytes freed.]
; [GC will next occur when at least 12,120,016 bytes are in use.]
; [GC threshold exceeded with 12,133,576 bytes in use. Commencing GC.]
答案 0 :(得分:5)
有两个函数是'tail recursive':sqr-iter和zint。
一般建议:
为了让编译器优化这些,DEBUG优化级别需要为2或更低。
您可以通过反汇编来查看编译器是否生成尾递归代码。使用函数disassemble
。
GC调用本身不是问题。大多数实现都不会在GC上打印任何内容。 CMUCL默认打印(IIRC)。 CMUCL可能会为未优化的代码分配大量的浮点数 - 这可能会导致很多GC。
只有当CMUCL有堆栈溢出时,您才会看到尾调用优化不起作用。地方选区本身只显示大量的消费。
因此,要调试您的问题,首先需要查看是否使用尾调用优化编译代码。你可以反汇编代码。另一种选择是在运行调试器时放入代码,然后查看堆栈回溯。在堆栈上不应该有大量的递归调用 - 它们将被跳转替换。
如果代码以恒定的堆栈空间运行,那么您需要查看浮点数的分配。您需要检查代码是否分配了太多的浮点数。
答案 1 :(得分:0)
我认为这取决于设置 ......这是相关的SBCL。
* (declaim (optimize (speed 0) (compilation-speed 0) (safety 3) (debug 3)))
* (defun foo () (bar))
* (defun bar () (foo))
* (foo)
INFO: Control stack guard page unprotected
Control stack guard page temporarily disabled: proceed with caution
INFO: Control stack guard page reprotected
#<sb-kernel::control-stack-exhausted {1002DBD4D3}>
但如果你告诉它你想要它快速而不用担心调试......它会做得更好
(declaim (optimize (speed 3) (compilation-speed 0) (safety 1) (debug 0)))
(defun foo () (bar))
(defun bar () (foo))
(foo) ;;; CPU topped out, but no stack issues or garbage collecting