Real closures

Klaus D. Witzel klaus.witzel at cobss.com
Sun Oct 8 08:11:23 UTC 2006


On Sun, 08 Oct 2006 08:46:11 +0200, J J wrote:
> Klaus,
>
> I noticed in the comment (thanks for pointing out the BlockClosure class  
> btw) it mentioned recompiling the whole image using the BlockClosure  
> class instead of the BlockContext.  Has this been tried?  Any guess what  
> the speed hit would be?

Not easy to predict. Here are areas that I've investigated:

1] instVar access; time(bytecode) versus: time(#instVarAt); assumption:  
time(VM,tempVar access) = time(VM,instVar access)

[|required|required]bench '1.88744451109778e6 per second.'

[|required|thisContext instVarAt:1]bench '1.37969322947873e6 per second.'

Cost increase: > 30%

2] # of blocks affected by instVar access; depends on what your image  
currently runs

BlockContext allInstances inject: 0 into: [:accum :each | each  
hasInstVarRef ifTrue: [accum + 1] ifFalse: [accum]]

=> 113

3] list of blocks with instVar access (see report below); ignore duplicate  
entries; seems that nothing much in the list looks time critical (except  
that the performance of the compiler, which is part of the list, for me  
counts as time critical ;-)

4] but the new music will play for those blocks which cannot be inlined by  
the new compiler, and figures in terms of before/after are not easy to  
obtain; except for a static cost analysis.

For the latter, both the new block valuation messages  
(BlockClosure>>#value and friends) and the frequency of #createBlock:  
versus: #blockCopy: are relevant; the bytecode access of temps is  
invariant-again the assumption that time(VM,tempVar access) =  
time(VM,instVar access). This paragraph is preliminary; I have as yet not  
thought about the cost of the implementation of ClosureEnvironment and of  
BlockClosure's non-local returns.

/Klaus

-------------------

|coll| coll := OrderedCollection new.
BlockContext allInstancesDo: [:each | each hasInstVarRef ifTrue: [coll  
add: each]].
coll do:[:each | Transcript cr; show: each printString]

=>

[] in WorldState>>displayWorld:submorphs: {[worldDamageRects := self      
drawWorld: aWorld     submorphs: submorphs    ...]}
[] in MenuItemMorph>>invokeWithEvent: {[(selArgCount := selector numArgs)  
= 0   ifTrue: [target perform: selector] ...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in MenuItemMorph>>invokeWithEvent: {[(selArgCount := selector numArgs)  
= 0   ifTrue: [target perform: selector] ...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in SystemWindow(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in AlignmentMorph(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in MenuItemMorph>>invokeWithEvent: {[(selArgCount := selector numArgs)  
= 0   ifTrue: [target perform: selector] ...]}
[] in WorldState>>displayWorld:submorphs: {[worldDamageRects := self      
drawWorld: aWorld     submorphs: submorphs    ...]}
[] in CornerRounder>>tweakCornersOf:on:in:borderWidth:corners: {[:i |   
corner := corners at: i.  saveBits := underBits at: i.  saveBits   if...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in SystemWindow(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in SystemWindow(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in ImageMorph(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in MenuItemMorph>>invokeWithEvent: {[(selArgCount := selector numArgs)  
= 0   ifTrue: [target perform: selector] ...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in MessageSet>>selectedMessage {[:class :selector |  class   ifNil: [^  
'Class vanished'].  selector first is...]}
[] in Parser>>parse:class:noPattern:context:notifying:ifFail: {[:ex |   
repeatNeeded := (requestor isKindOf: TextMorphEditor) not.  myStream...]}
[] in MenuItemMorph>>invokeWithEvent: {[(selArgCount := selector numArgs)  
= 0   ifTrue: [target perform: selector] ...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in ScrollBar>>menuButtonMouseDown: {[:sel |  menuSelector := sel.   
model perform: sel with: event]}
[] in MenuMorph(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in SystemWindow(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in AlignmentMorph(Morph)>>fullBounds {[:ex |  fullBounds := bounds.  ex  
pass]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in Delay>>schedule {[beingWaitedOn := true.  resumptionTime := Time  
millisecondClockValue + dela...]}
[] in SharedQueue>>nextPut: {[writePosition > contentsArray size   ifTrue:  
[self makeRoomAtEnd].  content...]}
[] in SharedQueue>>nextPut: {[writePosition > contentsArray size   ifTrue:  
[self makeRoomAtEnd].  content...]}
[] in SharedQueue>>next {[readPosition = writePosition   ifTrue: [self  
error: 'Error in SharedQueue s...]}
[] in SharedQueue>>next {[readPosition = writePosition   ifTrue: [self  
error: 'Error in SharedQueue s...]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in Parser>>parse:class:noPattern:context:notifying:ifFail: {[:ex |   
repeatNeeded := (requestor isKindOf: TextMorphEditor) not.  myStream...]}
[] in Encoder>>init:context:notifying: {[:variable |  node := VariableNode  
new     name: variable     index: (n := n...]}
[] in Encoder>>lookupInPools:ifFound: {[:sym |  (class bindingOf: sym)    
ifNotNilDo: [:assoc |     assocBlock value...]}
[] in Parser(Scanner)>>xDigit {[Number readFrom: source]}
[] in Encoder>>lookupInPools:ifFound: {[:sym |  (class bindingOf: sym)    
ifNotNilDo: [:assoc |     assocBlock value...]}
[] in WeakSet>>growTo: {[:each | (each == flag    or: [each == nil])    
ifFalse: [self noCheckAdd: ea...]}
[] in Encoder>>lookupInPools:ifFound: {[:sym |  (class bindingOf: sym)    
ifNotNilDo: [:assoc |     assocBlock value...]}
[] in Parser(Scanner)>>xDigit {[Number readFrom: source]}
[] in MethodNode>>generateWith:using: {[:m |  method := m.  method  
properties: properties.  ^ method]}
[] in BlockNode>>sizeForValue: {[:arg | size := size     + (arg  
sizeForStorePop: encoder)]}
[] in Encoder>>tempNames {[:node | (node isMemberOf: MessageAsTempNode)    
ifTrue: [scopeTable keyAtVal...]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in WorldState>>displayWorld:submorphs: {[worldDamageRects := self      
drawWorld: aWorld     submorphs: submorphs    ...]}
[] in WorldState>>displayWorld:submorphs: {[worldDamageRects := self      
drawWorld: aWorld     submorphs: submorphs    ...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in Parser>>parse:class:noPattern:context:notifying:ifFail: {[:ex |   
repeatNeeded := (requestor isKindOf: TextMorphEditor) not.  myStream...]}
[] in TextMorphForEditView(TextMorph)>>keyStroke: {[editor readKeyboard]}
[] in Parser>>parse:class:noPattern:context:notifying:ifFail: {[:ex |   
repeatNeeded := (requestor isKindOf: TextMorphEditor) not.  myStream...]}
[] in EventSensor>>eventTickler {[delay wait.  delta := Time  
millisecondClockValue - lastEventPoll.  (delta <...]}
[] in Encoder>>lookupInPools:ifFound: {[:sym |  (class bindingOf: sym)    
ifNotNilDo: [:assoc |     assocBlock value...]}
[] in Parser>>removeUnusedTemps {[:temp | (UIManager default     
chooseFrom: #('yes' 'no' )    title: ((temp ,...]}
[] in MethodNode>>generateWith:using: {[:m |  method := m.  method  
properties: properties.  ^ method]}
[] in BlockNode>>sizeForValue: {[:arg | size := size     + (arg  
sizeForStorePop: encoder)]}
[] in BlockNode>>sizeForValue: {[:arg | size := size     + (arg  
sizeForStorePop: encoder)]}
[] in Encoder>>tempNames {[:node | (node isMemberOf: MessageAsTempNode)    
ifTrue: [scopeTable keyAtVal...]}




More information about the Squeak-dev mailing list