Comments on Smalltalk block closure designs, part 1

Stephan Rudlof sr at evolgo.de
Thu Apr 26 19:08:43 UTC 2001


Allen,

thank you for your comments.

But in spite of your criticism of an evolution of the classical ST80 design
towards block closure semantics, I'm continuing to resist to give up my design
at the moment. For this case you have given a neat cookbook for implementing
block closure semantics, which I could follow then... ;-)

Some (especially Jitter) related questions and comments are following:

Allen Wirfs-Brock wrote:
> 
> At 11:15 PM 4/25/2001 +0200, Stephan Rudlof wrote:
> >Dear Allen and other Squeakers!
> >
> >Allen, I want to thank you for your interesting comments first!
> >
> >In the following I try to give some 'mapping' of the rules given by Allen to
> >my proposal. This should lead to a better understanding (for me, too!) of the
> >differences between these approaches. I try to describe which elements in the
> >BCSBlockContexts draft correspond to these rules. Please note, that there has
> >just been an update of my draft (triggered by Andreas Raab).
> >
> >Expression
> >         '->'
> >means
> >         'the former corresponds roughly to the following elements from
> > "Draft for
> >Block Closure Semantics for Squeak v0.7.0"'
> >.
> >
> >Allen Wirfs-Brock wrote:
> >
> ><snipped much of very interesting stuff>
> >
> > > I've now covered enough to explain how Smalltalk blocks with full closure
> > > semantics can be implemented an a way that never forces reification of the
> > > activation record stack:
> > >
> > >         Allocate all arguments and temps that are not "captured" by
> > blocks within
> > > stack allocated activation records.
> >
> >-> blockCopy if needed; it is needed if there starts a second evaluation
> >before finishing the first one;
> >-> stack allocated activation records correspond to *some* of the indexable
> >fields of the created BCSBlockContexts;
> 
> My statement above was talking about how the activation records (think
> MethodContext) for method and block invocations should be organized.
> Activation records are created as a side-effect of a method invocation or
> block evaluation.  blockCopy is the operation that creates a block object
> (a block closure), it has nothing to do with invocations. It appears to me
> that you are perpetuating the biggest "flaw" (from a closure semantics
> perspective) with the classic Smalltalk-80 design.  Block objects and block
> activation records are not the same things, you need two types of objects
> to represent them, not a single class that tries to be both. One way to see
> this is to look a them from a object-oriented design perspective...block
> closures and block activations have disjoint responsibilities and different
> cardinality, hence they should not be modeled by a single class:
> 
>          Block Closure
>                  Responsibilities
>                          Associate a pre-existing variable environment with
> a code block.
>                          Initiate evaluation of the code block in the
> context of the associated variable environment
>                  Cardinality
>                          One per block constructor evaluation
> 
>          Block Activation Record
>                  Responsibilities
>                          Provide storage for the arguments and local
> variable for a particular block evaluation.
>                          Record the continuation (the "return address") for
> a particular block evaluation
>                  Cardinality
>                          One per block closure evaluation.
> 

> For every block closure object that is created there may be N block
> activation record objects created. This cardinality mismatch is what causes
> the recursion/reentrancy problems for the classic Smalltalk-80 design and
> which forces you to use special logic to describe what happens if a
> BCSBlockContext is already active when it is evaluated.

Agreed. But the special logic isn't very complicated here and we save some
other logic needed by another design.

<snipped a very good cookbook for implementing block closure semantics with
another design>

> 
> > >         All variables that are subject to capture by blocks are explicitly
> > > allocated in heap allocated environment objects
> >
> >-> nothing like that
> 
> You can probably get by with static links that chain together activation
> records (MethodContexts) but this has the reification problem that
> ultimately is a performance killer.

This is a key point: Is this really a performance killer?

I don't know Jitter technology, so I'm very interested in a rationale for
this.

I'll try to argument against (with my limited knowledge, though).

Let's take the simple example, which should occur - as I guess - mostly often
in running an ST application in this or a similar form:
>          [:x :y | x >= y]

In the case of sorting a collection, where the elements don't reuse the same
block for implementing their >= operator, the advantage of having the
activation record inside the BCSBlockContext is, that this activation records
needs to be created only once; just by the block copy operation. Evaluation
always reuses this context then (this corresponds to the current design).

Lets assume a collection consisting of SmallIntegers (immediate objects):
Is for a Jitter creating and using an activation record onto the stack faster
than reusing an activation record at the heap for every evaluation in this
case?

And what about a more complicated one, where x and y are non immediate objects
residing *somewhere* onto the heap: they have to be accessed (possibly having
inst vars far away from their oop) to make the comparison, so I think there is
no localization of code/data in this case. What happens then (regarding
performance)?

Principal question: How intelligent are the caching mechanisms of typical
processors (for me its an Intel) regarding non localized memory addresses? Is
this a problem or not?

Any comments here are highly appreciated.

One last comment follows below...

> 
> > >                 (or, if "read-only", copied into the block object).
> >
> >-> same
> >
> > >                 (only activations that have captured variables need
> > allocate environment
> > > objects, except for the following rule)
> > >         The activation of a method that contains blocks with up arrow
> > returns
> > > always allocates a heap environment object.
> > >                 (it may be empty, in the sense that it contains no
> > variable, but it must
> > > be allocated)
> > >         Activation records reference ("point to") environment objects but
> > > environment objects never point to activation records.
> > >         Environment objects and block objects may reference other
> > environment objects.
> >
> >-> there are no environment objects at all (for returning BCSBlockContexts
> >have a home pointer, see below),
> 
> Your environment objects are implicitly part of your context objects
> 
> >-> outer temps reside in indexable fields of outer contexts,
> >-> outer temps are directly (oops for readonly vars) or indirectly (o/i pairs
> >for read/write vars) referenced by entries in indexable fields of actual
> >BCSBlockContext;
> >
> > >         A block (a closure) object references its enclosing
> > environment(s) (but
> > > never the enclosing activation record(s))
> >
> >-> there is the sender pointer to the next outer BCSBlockContext;
> >-> there is the home pointer to the MethodContext which has generated the
> >outmost BCSBlockContext (of the BCSBlockContext chain);
> >-> in addition outer contexts at arbitrary places in the BCSBlockContext chain
> >may be referenced by entries in the indexable fields of the actual context
> >(for read/write temps);
> 

> The home pointer should only be needed (in your design) if the block has an
> up arrow return.  Otherwise, the uplevel addressing mechanism should take
> care of everything else.

This is true and my design could be changed to save this storage in case of
non returning blocks. This would need more computation logic though.

Greetings,

Stephan

> 
> > >         A block object that implements an up arrow return captures a
> > reference to
> > > the environment associated with its method activation.
> > >                 (This is used as the continuation marker)
> >
> >-> home pointer
> >
> > >         An up arrow return is implemented by searching up the
> > activation stack for
> > > an activation record whose environment reference is the same as the
> > >                 continuation marker of the returning bock.  The
> > activation stack is
> > > trimmed to that activation record and a return is forced.
> > >                 (If no activation record with that environment is found
> > we have a
> > > #cannotReturn: situation)
> >
> >-> just return to home sender
> >
> >
> >I hope I haven't missed important things!
> >
> > >
> > > Enough for part #1, with luck I'll still be motivated to keep writing on
> > > part #2 tomorrow
> > >
> > > Allen
> > >
> >
> >That'd be nice!
> >
> >
> >Greetings,
> >
> >Stephan
> >--
> >Stephan Rudlof (sr at evolgo.de)
> >    "Genius doesn't work on an assembly line basis.
> >     You can't simply say, 'Today I will be brilliant.'"
> >     -- Kirk, "The Ultimate Computer", stardate 4731.3

-- 
Stephan Rudlof (sr at evolgo.de)
   "Genius doesn't work on an assembly line basis.
    You can't simply say, 'Today I will be brilliant.'"
    -- Kirk, "The Ultimate Computer", stardate 4731.3





More information about the Squeak-dev mailing list