Continuing the comments on garbage collection made by Aaron Sloman ...
Aaron writes:
>We thought hard about this and decided against implementing this or any
>other form of incremental garbage collection on the grounds that
>
>(a) the poplog garbage collector was already much faster than most of
>the others available then (one user at HP research labs was reported to
>have switched from a dedicated Lisp machine to Poplog lisp on a unix
>workstation simply because of the reduction in GC time, and having seen
>a GC in operation on a lisp machine at MIT around that time, I can
>believe it).
Yes! I still recall this with amusement. When the Symbolics hit a
"major" garbage collection it just went dead indefinitely. My colleague
Mike Prettejohn (now of Netcraft) phoned the vendors and asked how
long one of these major collections took. He was told, 'Oh, we've no
idea. When that happens we just turn the machine off and turn it
on again.' We never saw any major collection complete. Ever.
[Drift: I still hear people pedalling the claim that "special purpose"
computers failed because they couldn't achieve the volume necessary to
compete on price. My experiences with Lisp machines made me think that
the issue was much more complex than that. So it comes as no surprise to
me to see the current strong growth in special purpose machines.]
>(b) Any incremental garbage collection process slows everything down, so
>you have to be prepared to trade overall average or total speed for
>uniformity/smoothness of performance. I think John calculated that the
>extra checking for whether an item had or had not been moved from one
>space to another which has to be done on each reference could produce an
>overall reduction in performance of up to 50%. It would also double the
>*constant* space requirement of the heap, with possible consequential
>extra paging and swapping, whereas poplog now has its "heap" allocation
>increased only for the duration of the garbage collection, which is
>typically a small fraction of the total time (partly controllable by
>popgcratio).
May I recommend an excellent textbook "Garbage Collection" by Jones and
Lins. Amongst other things, this given strong coverage of the incremental
GC algorithms - interest in which has heightened as new algorithms have
been invented.
My only criticism of this book is that it pays insufficient attention to
the basic subject of avoiding the generation of garbage. One of the
practical strengths of Pop11 is that the open stack often plays the role
of an extensible vector - a role that in most other languages requires
expensive heap structures.
>We decided on balance that it was not worth moving to an incremental
>garbage collector.
In retrospect this seems like a far-sighted decision. The stop-and-copy
garbage collector implemented has several critical properties for
practical programming. One important property is that the time
taken for a garbage collection is proportional to the amount of
surviving store. The crucial consequence of this is that merely by
increasing the heap size the fraction of execution time consumed by
the garbage collector can be made as small as you like! What this
really means is that the "tuning problem" is trivial.
A serious defect with almost all more "sophisticated" garbage collection
techniques, such as generational garbage collection, is that they require
careful tuning to get best results. A good example in this regard was
the New Jersey SML compiler. The NJML team wrote some nice papers which
"proved" that their emphemeral garbage collector was so incredibly
efficient that it was quicker to manage stack objects via heap records than
an explicit stack. We ran the benchmarks and, yes, on those simple examples
NJML was astoundingly fast - faster than the equivalent C code - even though
the call stack was managed via explicit heap objects.
However, on our real-life program (which was a program transformation
system) the reality was that NJML quickly became intolerably slow. And
I don't mean that we were watching the second hand. I mean we were
hitting Ctrl-C after coming back from a long lunch.
By contrast, Poplog ML ran the exact same SML code without blinking. Yes
it did garbage collect many times a minute - at around 10ms per
collection.
The moral I drew from this experience was not that NJML was rubbish but
that it could not be tuned properly - even by a team of 5 experienced
programmers of which all had been intimately involved in the implementation
of garbage collectors.
> He did however introduce heap locking as an option,
>to reduce GC time: i.e. after compiling lots of things that you know
>will never become garbage because they are needed throughout the run of
>a program (e.g. compiled procedures, and maybe some large
>data-structures), you can call sysgarbage() to compact everything, then
>sys_lock_heap to lock it (see the help files for details), and the
>garbage collector will then not attempt to decide whether things in the
>locked part are garbage, and will not move them around (the non-garbage
>things still have to be looked at however to see what they refer to,
>since they can point to things outside the locked area).
>
>Heap locking works well, though if you repeatedly lock and unlock
>the heap while creating X windows you will lose memory at present,
>since non-relocatable stuff, such as C data structures, apparently
>do not become re-usable after garbage collections if they have once
>been part of a locked heap. I don't know whether that's a minor bug
>in the implementation or a hard problem to avoid. But that just
>means you should delay calling sys_lock_heap till the last moment
>if you are using X.
My single recommendation - do not repeatedly lock and unlock the heap.
It is not really possible to get this right. It is a problem for
real generational garbage collectors.
>This means that if you want to use pop-11 for real-time applications
>where the time tolerances are very much smaller, you have to use a
>special version of Unix and you have to try to write your programs so as
>not to generate garbage collections, e.g. re-using vectors, lists, etc.,
>or to prevent them occurring during critical intervals by making sure
>there's a GC just before the interval and the heap is big enough, etc.
An alternative to incremental garbage collection is speculative garbage
collection. If I ever get the time I will add it to Poplog. The basic
idea is to perform a stop-and-copy that can be abandoned instantly - as
soon as a signal arrives. [I've never seen this in the literature, I
came up with the idea a couple of years ago, but I rather doubt it is
original to me; it is too obvious.]
Is it possible to perform a speculative stop-and-copy (Cheney) garbage
collection? Because the copy phase destructively updates the current
heap (from-space) it is a little puzzle to work out how it should be
done.
Here's a simple method: take a fresh copy of the relevant part of the
heap - it is easy to see that this can be done speculatively. Now
perform a speculative Cheney-style copy collection into to-space. If
you've done all that without an interrupt, switch the from and to
spaces. Note that the speculative copy-collection must adjust for the
offset between the original heap (from-space) and its speculative
clone (cloned-space).
Of course, this argument does not require a copy of the entire heap;
the relevant part of the heap is the unlocked part. This is typically
only a few MW of store. [If you are interested in having a go, here are
a some helpful hints. It is easy to do better than the algorithm I've
outlined above. To avoid cloning the unlocked region of the heap you
can use a hash-table to store the forwarding pointers. An alternative is
to pad each heap object with a spare word. This padding can be
optional and dynamically eliminated when the heap grows "too big". Being
"too big" is a tuning parameter - bad news - but relatively simple to
define in terms of available RAM. Lastly, it is important to consider
how to manage potentially uncontrolled destroy actions.]
It is clear that speculative garbage collection would eliminate
garbage collection "glitches" from a vast range of programs that interact
with people. While waiting for user-events, garbage collection would
silently take place - might as well use the wait for something useful.
The same would apply to servers, too. Let's suppose you write a web
server in Poplog (like my Palantir server) then when the server is waiting
for some request to happen it can do a nice tidy-up.
--
Steve
|