Re:Boehm GC の Repeated allocation of very large block 問題 その3

解決した。結局やった事は以下の通り。
(1) GMP の allocator には GC のものは使用せず、malloc, realloc, free を使用する
(2) Bignum, Ratnum は gc_cleanup を継承し GC 時にデストラクタが呼ばれるようにする
(3) Bignum, Ratnum のデストラクタで mpz_clear, mpq_clear を呼ぶ
(4) realloc に hook して GC_gcollect を呼ぶ


(1) は GMP の mpz_t が生成する "false pointer" を GC の感知しない領域に追い出すため
(2), (3) は不要になったタイミングで mpz_clear などが確実に呼ばれるようにするため
(4) は enami さんの指摘で分かったのだが、GCmalloc/realloc が呼ばれた回数、割り当てたサイズを知らないので、適切な GC タイミングを教えるという意図。
現状では以下のように少なくとも 30 MB 毎に GC されるようになっている。(この値はいくつか実験をして適当に決めた)

static void* gmp_realloc(void *ptr, size_t oldSize, size_t newSize)
{
    static uintptr_t totalSize = 0;
    totalSize += newSize;
    if (totalSize > 30 * 1024 * 1024) {
        GC_gcollect();
        totalSize = 0;
    }
    return realloc(ptr, newSize);
}


またついでに無駄な mpz_t を作らないように、かなりがんばったので、以前よりだいぶ速くなったと思う。

% mosh -v
Mosh R6RS scheme interpreter, version 0.1.2 
sewashi% mosh
mosh>(define (fact n)
             (let f ((n n) (r 1))
               (if (< n 2) r
                  (f (- n 1) (* r n)))))
#<unspecified>
mosh>(time (and (fact 100000) #t)) 

;;9.126263 real 8.396524 user 0.676042 sys
#t

というわけで約 9 秒になった。これは劇的に速い。(ほとんど GMP のおかげなんですが)
Ikarus の trunk で試したら 6.5 秒だったので VM としてはかなりがんばっている感じかな。