■
昨日の続き。
dynamic-windを使えば、昨日の
他の問題点としては、S式バトラーのように、複数のevalを同時に起動して、継続を使ってコルーチン的に動きまわるような場合には、正常にメモリ使用量を取る事が出来ない。
を一応、解決できる事に気付いた。
(但し、運が悪ければ、メモリ使用判定が片方のevalにばかり偏ってしまう可能性は残るが)
(use eval-sv) (use gauche.parameter) (use util.list) (define-values (eval/sv env) (make-eval/sv :isolate-port? #f)) (define start-size (make-parameter #f)) (define runup-size (make-parameter 0)) (define limit-size (make-parameter #f)) (define (get-used-memory) (let1 stat (gc-stat) (- (car (assoc-ref stat :total-heap-size)) (car (assoc-ref stat :free-bytes))))) (define (sv type symbol expr args return except) (define (over?) (let ((now-used (- (get-used-memory) (start-size))) (old-used (runup-size)) ) (< (limit-size) (+ now-used old-used)))) (when (over?) (gc) (when (over?) (except "out of memory" (get-used-memory)))) (apply expr args)) (define (eval/memlimit expr limit) ;; 諸事情により、parameterizeは使わずに、自前でparameterを管理する (let ((current-runup-size 0) (original-start-size #f) (original-runup-size #f) (original-limit-size #f)) (dynamic-wind (lambda () ;; parameterizeする (set! original-start-size (start-size)) (set! original-runup-size (runup-size)) (set! original-limit-size (limit-size)) (gc) (start-size (get-used-memory)) (runup-size current-runup-size) (limit-size limit) ) (lambda () (eval/sv expr sv)) (lambda () (gc) ;; current-runup-sizeに、今回の処理で増えたメモリ増分値を足しておく ;; (継続再起動時の為に) (set! current-runup-size (+ current-runup-size (- (get-used-memory) (start-size)))) ;; unparameterizeする (start-size original-start-size) (runup-size original-runup-size) (limit-size original-limit-size) ;; 無駄に参照を握らないように、明示的に解放しておく (set! original-start-size #f) (set! original-runup-size #f) (set! original-limit-size #f) ))))
簡単に動作確認を取る。
(複数のevalをコルーチン的に同時に動かす動作確認は、面倒なので省略する。)
(eval/memlimit `(let baibain ((kuri-manjuh '(1))) (display (,start-size)) (display " ") (display (,runup-size)) (display " ") (display (,limit-size)) (display " ") (display (,get-used-memory)) (display " ") (display (length kuri-manjuh)) (newline) (baibain (append kuri-manjuh kuri-manjuh))) 10)
3678208 0 10 3678208 1 3678208 0 10 3678208 2 3678208 0 10 3678208 4 3678208 0 10 3678208 8 3678208 0 10 3678208 16 3678208 0 10 3678208 32 3678208 0 10 3678208 64 3678208 0 10 3678208 128 3678208 0 10 3678208 256 3678208 0 10 3678208 512 3678208 0 10 3678208 1024 3678208 0 10 3678208 2048 3678208 0 10 3678208 4096 3678208 0 10 3678208 8192 3678208 0 10 3678208 16384 *** ERROR: out of memory 3682304 Stack Trace: _______________________________________
一見、正常に動作しているように見えるが、何度も実行すると、昨日のバージョンとは違い、少しずつメモリ使用の閾値が増えていってしまっている。
(何度も実行した後の結果)
5369856 0 10 5369856 1 5369856 0 10 5369856 2 5369856 0 10 5369856 4 5369856 0 10 5369856 8 5369856 0 10 5369856 16 5369856 0 10 5369856 32 5369856 0 10 5369856 64 5369856 0 10 5369856 128 5369856 0 10 5369856 256 5369856 0 10 3690496 512 5369856 0 10 3690496 1024 5369856 0 10 3690496 2048 5369856 0 10 3690496 4096 5369856 0 10 3690496 8192 5369856 0 10 3690496 16384 5369856 0 10 3690496 32768 5369856 0 10 3784704 65536 5369856 0 10 4313088 131072 5369856 0 10 5365760 262144 *** ERROR: out of memory 7442432 Stack Trace: _______________________________________
これを見る限りでは、(gc)のタイミングではなく、実際に何らかの処理を行っている最中に、使用メモリが突然減っているように見える。
これが原因で、二回目以降に実行したeval/memlimit起動時に、使用メモリの底状態の測定ができずに、結果として正しく機能しなくなってしまっている。
どうしたものか……。
Gauche単体としては、メモリの回復量だけを見るならば、メモリリークはしていないようなので、(gc)と(gc-stat)を使うのは非推奨という事なのか。リファレンスにも書かれてないし。
駄目だったら、やっぱりSCM_MALLOCに手を入れるしかなさそうだ……。
あとでソース見る。
追記:
簡単にソース見た。
src/extlib.stub にあるのは、
(define-cproc gc () (call <void> "GC_gcollect"))
なので、別に(gc)が無効化されている様子は無かった。