On 24/05/2010 23:02, Marco Túlio Gontijo e Silva wrote:
Hi.
I was accepted as a student to the Google Summer of Code to work on
Implemmenting the Immix Garbage Collector in GHC. I'm currently reading the
Commentary and the source code in order to understand what needs to be
changed. I have some questions which are not central to my work, but I thought
I should ask them along with the ones that are, since they may be a source of
doubt that would make it harder for me to work in the code.
-------------------------------------------------------------------------------
From http://hackage.haskell.org/trac/ghc/wiki/Commentary/Rts/Storage/BlockAlloc
:
(...)
Currently, megablocks are never freed back to the OS, except at the end of
the program. This is a potential improvement that could be made.
What's the problem with freeing megablocks to the OS? Why isn't it done
already?
No big problems, except that you have to be slightly careful to avoid
thrashing - freeing too much and then having to re-allocate it again
almost immediately, which is easy to get wrong with a copying GC because
memory use spikes during a GC.
Ian is looking into this, I believe:
http://hackage.haskell.org/trac/ghc/ticket/698
(...)
From Faster laziness using dynamic pointer tagging:
(...)
Return adresses have info tables just as heap closures do; this makes stack
frames have almost exactly the same layout as heap closures. The info table
for a return address describes the layout of the stack fram for the garbage
collector, just as the info table for a heap closure describes the layout of
the closure.
Is the GC responsible for collecting the Stack objects, or it only scavenges
them to find pointers to Heap objects?
A stack is part of a TSO object, and TSOs are managed by the GC just
like other objects. Stacks and hence TSOs are mutable, of course.
(...)
From includes/rts/storage/GC.h:
(...)
typedef struct generation_ {
unsigned int no; // generation number
Why does the generation hold its own number? Can't it be obtained by "this" -
generations?
It's just a convenience.
(...)
unsigned int n_words; // number of words
Is this the total number of words in the all the blocks? Can't it also be
obtained by n_blocks * (1<< BLOCK_SHIFT)?
No, because most blocks are not completely full.
(...)
extern generation * g0;
extern generation * oldest_gen;
Can't generations[0] and generations[RtsFlags.GcFlags.generations-1] be used?
Again, just convenience.
(...)
From rts/sm/GC.c :
(...)
nat gens = RtsFlags.GcFlags.generations;
I've seen some times this kind of construction in GHC, where a variable is
create for an assignment like this, and never changed again. Are these kind of
constructions used only for efficiency?
Probably just to save typing! If gens is never modified, the
declaration should really have a 'const' modifier on it.
(...)
From rts/sm/BlockAlloc.c:
(...)
- most allocations are for small blocks
The block size is constant, so this should be for a small number of blocks,
instead of for small blocks?
Yes.
(...)
-------------------------------------------------------------------------------
Ok, these were the questions which are not central to my work. The central
question I have now follows.
Please correct me if I'm wrong, but it seems to me that the memory allocator
goes as follows:
_________
( Program )
---------
^
|
+------------------+
| Object allocator |
+------------------+
^
|
+-----------------+
| Block allocator |
+-----------------+
^
|
+----------------------+
| Mega block allocator |
+----------------------+
^
|
____
( OS )
----
Yes; I presume by the "Object allocator" you mean all the parts of the
storage manager that sit on top of the block allocator, including the
garbage collector.
I could find documentation about the Mega block allocator and the Block
allocator, but not of the Object allocator. Actually, I made it up myself,
since I saw nothing written about it, but I wonder that there should be some
code that allocates objects in a block and request a new block when one is
needed.
The mutator allocates objects inline, by bumping the heap pointer (Hp).
Allocation of objects is such a common operation it needs to be
incredibly cheap.
I think that the first thing I should do is create a Line allocator, between
the Block allocator and the Object allocator. But for this, I need to know
where the usual object allocation is done.
I don't think we need a separate Line allocator. Let's talk about the
high level design, here's what I had in mind:
The mutator stays exactly as it is. We're going to use Immix for the
old generation only, in the same way that mark-compact and mark-sweep
are possible collection strategies for the old generation right now.
To make things really easy, we could even leave the marking phase
exactly as it is, using a mark bitmap. The only things we need to
change are:
1. the way that we free memory from the old generation after
a major GC, and
2. the way that we allocate memory for copying objects during
minor and major GC
We want to free memory in units of a Line, and allocate memory from the
pool of available Lines (or from the block allocator if no free Lines
are available).
To obtain free Lines, we can walk over the bitmap after a major GC and
identify any free Lines. Remember that we only mark the start of an
object, so an object may extend into the next Line (let's limit
ourselves to objects no larger than a Line for now), so a Line is only
empty if the Line preceding it is also empty.
Then we need a representation for the free list of Lines, probably just
link them together in a list within a block. The blocks in the old
generation are already on a list attached to the generation, so we just
need a pointer to the first free Line from the generation.
That covers (1). For (2), you'll need to change the way that to-space
memory is allocated, but only for the old generation. If there is a
free Line then use that. We'll also need to be able to allocate space
for objects larger than a Line (or maybe larger than a certain fraction
of a Line, to avoid too much fragmentation). Identifying objects that
are larger than a Line can be done manually, but it will probably be
better in the long run if these objects have a special flag in the info
table.
Ok, that's a sketch of what I had in mind, there are quite a few gaps
still to be filled in, and I'm sure you'll encounter some things I
haven't considered.
Another thing: is this the right list to this kind of question? Or should I
mail glasgow-haskell-users?
Either list is fine...
Cheers,
Simon
_______________________________________________
Cvs-ghc mailing list
Cvs-ghc@haskell.org
http://www.haskell.org/mailman/listinfo/cvs-ghc