1

このコードが 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.]
4

2 に答える 2

5

'末尾再帰'である2つの関数があります:sqr-iterとzint。

一般的なアドバイス

コンパイラにこれらを最適化させるには、DEBUG最適化レベルを2以下にする必要があります。

コンパイラーが末尾再帰コードを逆アセンブルすることで生成するかどうかを確認できます。関数を使用しますdisassemble

GCの呼び出し自体は問題ではありません。ほとんどの実装はGCに何も出力しません。CMUCLはデフォルトでそれを印刷します(IIRC)。CMUCLは、最適化されていないコードに大量のフロートを割り当てる可能性があります。これにより、多くのGCが発生する可能性があります。

CMUCLにスタックオーバーフローがある場合にのみ、末尾呼び出しの最適化が機能していないことがわかります。GC自体は、大量のconsingのみを示します。

したがって、問題をデバッグするには、最初に、末尾呼び出しの最適化をオンにしてコードがコンパイルされているかどうかを確認する必要があります。コードを分解できます。もう1つのオプションは、デバッガーの実行中にコードを配置してから、スタックバックトレースを確認することです。スタックには、再帰呼び出しが多数存在しないようにする必要があります。これらはジャンプに置き換えられます。

コードが一定のスタックスペースで実行されている場合は、floatの割り当てを確認する必要があります。次に、コードがあまりにも多くのfloatを割り当てていないことを確認する必要があります。

于 2013-03-15T16:28:09.353 に答える
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
于 2013-04-24T19:29:48.283 に答える