On Tue, May 15, 2012 at 12:34 PM, Raoul Duke <[email protected]> wrote:

> On Tue, May 15, 2012 at 12:23 PM, Jonathan S. Shapiro <[email protected]>
> wrote:
> > These are known as unboxed types. Automatic stack allocation is a good
> > thing, but it should be done generally. What you need to do this is
> escape
> > analysis, which is *not* a simple inference problem in the general case.
> > Conservative escape analysis is often good enough in practice.
>
> as an offshoot of looking for something like BitC, thinking about what
> might be "good enough" for doing e.g. games, i think i would like to
> have more manual controls over the GC and less magic like escape
> analysis that i have to rely on...


Hang on there. I didn't say that automatic stack allocation should be the
only mechanism. In fact, I've argued very strongly elsewhere that relying
exclusively on automatic stack allocation for critical system software is
completely unacceptable. Eike said that he contemplated a simple scheme for
stack allocating certain kinds of structures: closures and dictionaries.

It turns out that one of his two examples - closures - is actually not so
easy to unbox automatically because of the need for escape analysis. Once
he has to do escape analysis, he should consider automatic stack allocation
more generally. But note that all of this is completely invisible at the
source level; it doesn't impact the externally visible semantics, and
therefore can be deferred for later implementation.

Concerning manual controls over GC: I understand why you *want* manual
control over GC. Unfortunately, the price is something you won't be willing
to pay. The price of manual GC control is the requirement that the
application tolerate out of memory exceptions as a routine case inside
critical algorithms. For this and several other reasons, I would argue that
what you should really be seeking is the ability to write allocation-free
critical code.

And yes, I know that isn't possible in all cases, and I'm aware of the work
on region-structured memory management. The problem with the
region-structured approach is that the usual implementation doesn't address
fragmentation concerns, and there are cases where regions don't get
collected eagerly enough - notably due to loops.


Today's hack is tomorrow's backwards compatibility nightmare. The lower you
go in the software stack, the bigger the impact of a compatibility
nightmare is. Language design sits *very* low in the functional stack.
Tread carefully.

e.g. random
> thought, being able to say "please check to see if you can gc any of
> the things that were heap allocated during this function call" could
> be nice.


There are three possible implementations:

1. Implement it by performing a full GC of new-space (in which case you
should just call that)
2. Implement it by region analysis, discarding the function-local region.
In practice this may not turn out to release any storage [see below].
3. Implement it by escape analysis and aggressive stack allocation. This
approach works, but it has a cost [see below].

In order to understand why "discard by region" isn't attractive, we need to
take note that the goal is less about releasing current storage than it is
about being able to allocate new objects from the storage that we release.
A consequence of this is that discarding a region isn't useful unless it
happens to fall at the very end of the heap, which is something that is
very difficult to ensure. If it *doesn't* fall at the end of the heap, then
you can't unwind the next-allocation pointer, and you won't be able to
actually reclaim that space until the next generational GC (which would
have reclaimed it anyway).

The alternative is to do some stack-like region management strategy that is
*separate* from the heap so that it doesn't interfere. You don't want to do
this on the actual stack. The problem is that a FOR loop can cause a lot of
allocations from region R in each pass, most of which become garbage in the
next pass. It's not that hard to end up in a state where you are forced to
GC the region, which is just what you were trying to avoid.

The aggressive stack allocation approach works, because it allocates
storage from a place that doesn't interfere with GC (that is: from the
stack). It requires that all of the stack-allocated storage be non-escaping
- and particularly that it not escape into other threads of control. The
hidden "gotcha" is that it causes stack frames to grow, which makes
incremental and/or concurrent collection harder.


I won't pretend to understand all of the region-related issues here. As I
wrote the argument above, it occurred to me that it should be possible to
identify per-region "roots", and thereby to perform small compacting
collections on a region by region basis. The "small" part makes the
collections relatively fast. The compacting is necessary in order for
re-allocation to work properly.


shap
_______________________________________________
bitc-dev mailing list
[email protected]
http://www.coyotos.org/mailman/listinfo/bitc-dev

Reply via email to