[MLton] callcc and space use
Matthew Fluet
fluet at tti-c.org
Thu Feb 21 02:40:46 PST 2008
On Wed, 20 Feb 2008, Vesa Karvonen wrote:
> On Thu, Feb 14, 2008 at 5:09 PM, Matthew Fluet <fluet at tti-c.org> wrote:
> [...]
>> There was a (short) discussion of stack resizing policy a few years ago:
>> http://mlton.org/pipermail/mlton/2004-April/025297.html
>>
>> I suspect that it will be difficult to have a one-size-fits-all policy.
>> When shrinking is too aggressive, you pay to grow the stack when a stack
>> is resumed. When shrinking isn't aggressive enough, you pay in space and
>> GC time for the wasted space. If you have a small number of preemptively
>> scheduled threads, then no thread is idle for too long, and it makes more
>> sense to not resize stacks too aggressively. If you have a large number
>> of idle threads, then it makes sense to resize the stacks more
>> aggressively.
>
> Sounds reasonable, but, even if it might not be optimal for all cases,
> I think that it would be least surprising to have a default resizing
> policy that ensures that the reserved stack space is not more than a
> constant factor of the used space. Otherwise it would not be
> difficult to construct programs that appear to have a space leak.
True. That has the nice property that successive GCs can't free up more
than a constant factor of space used by stacks. ('Course, if there are
O(n) stacks (rather than O(1)), then successive GC's will appear to free
up O(n) space, but still a constant factor per stack.)
>> On the other hand, your policy of requiring a paused stack to have have
>> reserved <= 4 * used seems reasonable. But, with that policy, does it
>> matter whether there is the additional policy that we halve the reserved
>> size each time a paused thread lives through a GC?
>
> I think that just a hard limit of 4*used wouldn't be enough, because
> then long lived paused threads would never be minimized.
O.k. But, that is a stronger property than simply ensuring that the
reserved stack space is not more than a constant factor of the used
space.
I sketched out the following as a possible resizing policy, which gives
some more controls while trying to maintain the current behavior of
single-threaded programs:
size_t sizeofStackGrow (GC_state s, GC_stack stack) {
size_t res;
res = max ((size_t)(s->controls.ratios.stackCurrentGrow * stack->reserved),
sizeofStackMinimumReserved (s, stack));
return res;
}
size_t sizeofStackShrink (GC_state s, GC_stack stack, bool current) {
size_t reservedMax, reservedShrink, reservedMin, reservedNew;
if (current) {
/* Shrink current stacks. */
reservedMax =
(size_t)(s->controls.ratios.stackCurrentMaxReserved * stack->used);
size_t reservedPermit =
(size_t)(s->controls.ratios.stackCurrentPermitReserved * stack->used);
reservedShrink =
(stackReserved <= reservedPermit)
? stack->reserved
: (size_t)(s->controls.ratios.stackCurrentShrink * stack->used);
reservedMin = sizeofStackMinimumReserved (s, stack);
} else {
/* Shrink paused stacks. */
reservedMax =
(size_t)(s->controls.ratios.stackMaxReserved * stack->used);
reservedShrink =
(size_t)(s->controls.ratios.stackShrink * stack->reserved);
reservedMin = stack->used;
}
reservedNew =
alignStackReserved
(s, max(min(reservedMax,reservedShrink),reservedMin));
/* It's possible that reservedNew > stack->reserved for the
* current stack if the stack invariant is violated. In that
* case, we want to leave the stack alone, because some other
* part of the gc will grow the stack. We cannot do any growing
* here because we may run out of to space.
*/
assert (current or reservedNew <= stack->reserved);
reservedNew = min (stack->reserved, reservedNew);
return reservedNew;
}
with the following defaults:
s->controls.ratios.stackCurrentGrow = 2.0;
s->controls.ratios.stackCurrentMaxReserved = 32.0;
s->controls.ratios.stackCurrentPermitReserved = 4.0;
s->controls.ratios.stackCurrentShrink = 0.5;
s->controls.ratios.stackMaxReserved = 8.0;
s->controls.ratios.stackShrink = 0.5;
The difference from the current policy is that both the current and paused
stacks have a maximum reserved size that is a ratio of the used size.
The other difference is that a lot more ratios can be set by runtime
options.
More information about the MLton
mailing list