Mike Lambert wrote:

> Should this be a configure.pl-determined constant? Should we hardcode it
> to sizeof(void*)? Is this behavior guaranteed by the C spec? Can we
> assume it across all platforms even if it is not guaranteed?

I would be in favour of making it configuration-determined, just in case 
there is some platform that likes a packed stack - but we can probably 
get away with a fairly simple test, as I think we're unlikely to find many 
exceptions.

> Might I ask what your motivation was for the header linked list? 

The original aim of the first version of grey was to find further uses 
for the interpreter cycle count. As mentioned before, only parts of
that code have been implemented in the current version of grey.
I think perhaps a summary of the first version of grey may be in
order (that is the one that does 5000 lives in 60 seconds on my
box; still by far the fastest)

1) Decouple dead-object detection from dead-object freeing.
In my profiling, I discovered that the process of actually freeing
the dead objects was a fairly lengthy part of the DOD process,
being proportional to the total number of objects allocated.
Although the DOD scan has to go through everything, there is
no need to free objects in a pool that is not under pressure;
instead that can be delayed until a specific pool needs more
resources. By changing the cycle count field in each header
from its original meaning of 'birthdate' to 'last live' and setting
it to the current interpreter cycle when marking a header as
live, that information remains available for later use - so when
a resource pool's free list is empty, we can scan for resources
that have a 'last live' field before the cycle count when the
last dod scan was performed. Of course, a full dod scan has
to be forced whenever the counter wraps.

My next thought was that this could be integrated into memory
pool compaction - when a pool is compacted, we could simply
ignore dead headers, but it would be better to actually put them
onto the free list. 

At this point I came up with the idea of allocating pool memory
in pages instead of a single large chunk. The long time goal
was to aim for a pseduo-generational collection method, the
short time goal was to reduce the peak memory usage by not
requiring having double the pool memory allocated during the
compaction run. This only works if compact_pool can not only
allocate memory in pages, but also free each page as it has
been processed. This is only possible if compact_pool runs
through the pool, rather than through the headers - but we
had no link from pool to header - thus I invented the linked list
of buffers by pool page. Then the compaction process 
becomes: allocate one new page of memory at the start,
loop through each 'old' page, allocating new pages as
required; free each 'old' page once completed. The allocation
and freeing of memory pages was then changed so that
old pages were not returned to the system, but stored in a 
free page pool, from where they were reallocated. This has
another benefit in reducing the interaction with the system
memory allocator.

Initially these lists were simply connected to each memory
page, without specific sequencing - then I realised that by
sorting them in bufstart sequence, I could implement a
different version of COW, with no further impact on the
String header (of course, it already has the cycle count,
next/prev header pointers and the pointer to the header
pool - so it is way more expensive than strstart). As you
pointed out, this method of COW allows a fairly effective
means of decowing; as well as the example you mention,
it can also handle the more complicated case of:
   set S0, some_large_data_file_contents
   substr S1, S0, 0, 1 #get first character as COW
   substr S2, S0, 214761, 1 #get last character as COW
   set S0, ""
   sweep
   collect
[Aside: I would like that 'set S0, ""' to be at least 'clear S0' or 
better 'free S0', where the former would zero the register, and the
latter would return the header to the free pool immediately]

The page-by-page compaction process has the ability
to be suspended at the end of any page; this could be
used in either a round-robin type approach, starting
from the oldest page, and stopping when we achieved
a target amount of free space; or, if we suspect that a
lot of pool usage is short life span, we could always
start with the most recently filled page and would back.
I have not yet tried any of these approaches - I always
just did a full compaction pass.

Hopefully this explains how the linked list approach came
about. Remember, grey has always been experimental -
the above does not mention at least a dozen ideas that
caused performance to deteriorate and were discarded.
Only changes that improved performance (or had a very
small detrimental effect but were required for subsequent
changes) survived.
-- 
Peter Gibbs
EmKel Systems


Reply via email to