[D-runtime] Metadata for memory

Steve Schveighoffer schveiguy at yahoo.com
Fri Jul 30 06:53:07 PDT 2010


People are working diligently on incorporating precise scanning into the GC.

I have added safe array appending.

All of these require metadata associated with the memory block to assist the 
runtime in correctly performing it's tasks.  So far, the solution has been to 
eat a small piece of the data block allocated.  For precise scanning, it's a 
bitmask or pointer-to-bitmask to indicate which fields are pointers.  For safe 
array appending, it's a length field indicating the "used" length of the block.

One of the problems the developers working on precise scanning have pointed out 
is that we are both eating up the memory block, and there is the potential that 
we are both trying to use the same part of the memory block, unaware of the 
other's usage.  Also, in large blocks I store the array length at the beginning 
of the block, which can screw up the precise scanning, since the length can 
offset the alignment of the bitmask.

I think it's probably time to think about how we can allocate metadata in an 
orderly fashion.

The requirements are that:

1. the overhead is as low as possible.  Array appending uses 1 byte for all 
blocks <= 256 bytes.  A bitmask for a small block (16 bytes) only needs 3-4 
bits, but may consume 4 bytes if a pointer to a bitmask is required. 

2. alignment of data is preserved.  This is done mostly via putting the data at 
the end.  But storing outside the block is also a possibility.
3. Individual Metadata does not conflict with others.

I think the GC is going to have to be aware of the array appending, since it 
must be aware of the length field (if stored int he block), and it could 
potentially save many cycles only scanning 'used' data.

On storing the array append length at the beginning of large blocks:  A large 
block can be extended in place, meaning that the block size (not just the used 
size) can increase.  This means the end of the block can move.  With my current 
array append code, for shared array appends, you would have to lock the entire 
append function to ensure this didn't happen in the middle of the append.  I 
query for the block size once, and if it changed after I do the query, the 
length would be screwed up.  This might be a necessary evil but who is appending 
large amounts of data to shared blocks anyways?  So biting the shared bullet and 
locking the entire shared append function might take care of the issues of 
storing at the beginning (scanning skipping the length, bitmask offset by n 
bytes).  The GC is going to have to be aware of moving the bitmask anyways.

I think the most funky situation is really the small blocks (16 or 32 bytes) 
where you want overhead to be small, but you need to store enough info.  With a 
16-byte block, I think we can squeeze both the bitmask and length into one 
byte.  Essentially 4 bits for length, and 3 bits for a bitmask (the 4th word is 
incomplete, so only 3 bits are needed).  For a 32 byte block, we would need 2 
bytes, one for the 7-bits bitmask, and one for the length.  At 64 bytes, we can 
have 16 bits for bitmask, and 1 byte for length.  At 128 bytes and above, a 
bitmask would consist of 32 bits anyways, so might as well start using a pointer 
(may be different for 64-bit CPUs), and still only a byte is needed for length.

The order of storage should always be arraylength, bitmask to keep alignment 
proper, and allow the most usage of space.  And depending on the bits set on the 
block (APPENDABLE, NO_SCAN), one or both of these may be omitted.

We also should get bugzilla 4487 addressed, allocating an "array" whenever a 
single struct is desired is wasteful.

Anyone have any other ideas?  Any other points to make?

-Steve



      


More information about the D-runtime mailing list