S.Ducasse 1
QuickTime™ and aTIFF (Uncompressed) decompressorare needed to see this picture.
Stéphane Ducasse
Stephane.Ducasse@univ-savoie.fr
http://www.listic.univ-savoie.fr/~ducasse/
Blocks and Optimization
in VW
S.Ducasse 2
License: CC-Attribution-ShareAlike 2.0
http://creativecommons.org/licenses/by-sa/2.0/
S.Ducasse 3
[ :x :y | |tmp| ...]
value
value:
value: value:
value: value: value:
valueWithArguments:
•InVisualWorks there are four types of blocks:
• Full Blocks
• Copying Blocks
• Clean Blocks
• Inlined Blocks
•The programmer does not have to explicitly mention.
•Inferred by the compiler. However, knowing the subtle
differences allows the programmer to write more efficient
code.
Blocks and Optimization inVW
S.Ducasse 4
• Read and assign temporary variables.
• Block containing explicit return ^.
• Compiled in a BlockClosure.
• Evaluation by the creation of an explicit
MethodContext or BlockContext object instead of
using a pseudo-object contained in the stack.
• Most costly
• Instead of:
• m1: arg1 m1: arg1
• arg1 isNil ^ arg1 isNil
• ifTrue: [^ 1] ifTrue: [1]
• ifTrue: [^ 2] ifTrue: [2]
Full Blocks
S.Ducasse 5
• Read temporary variables but do not assign them.
• No explicit return.
• Access instance variables of self and assign them.
• Not compiled into a BlockClosure.
• They are compiled by copying every access into the
block, thus avoiding explicit references to a context
where the copied variables appear.
• Their arguments and temporaries are merged into the
enclosing method’s context as “compiler-generated
temporaries”.
Copying Blocks
S.Ducasse 6
• Contain only reference block temporary variables or
global variables.
• No reference to self or to instance variables.
• nodes do: [:each | each name = #stef]
• nodes select: [:each | each isLocal]
Clean Blocks
S.Ducasse 7
• Code of certain methods, like whileFalse: ifTrue:, is
directly inlined into the code of the calling method.
• The literal blocks (without arguments) passed as
argument to such methods are also inlined in the
byte-code of the calling method.
• Inlined methods are whileTrue, whileTrue:, whileFalse,
whileFalse:, and: or:, ifTrue:, ifFalse:, ifTrue:ifFalse:,
ifFalse:ifTrue:, to:do:, to:do:by:
• Look in MessageNode>>transform* methods to see
the inlining
Inlined Blocks
S.Ducasse 8
• testInLined
• 1 to: 5 do: [:x| ]
• Compiled into :
• | t1 |
• t1 := 1.
• [t1 <= 5] whileTrue: [t1 := t1 + 1].
• But no BlockClosure is created (look into the byte
codes)
Inlined Blocks
S.Ducasse 9
• Instead of:
• |t|
• [:x | t := x foo] value: 1.
• t := t * 2.
• ^t
• The reference to t inside the block makes it at least a
copying block.
• t := makes it full.
• With the following we have a clean block.
• |t|
• t := [:x | x foo] value:1.
• t := t * 2.
• ^t
From Full to Copy
S.Ducasse 10
• Full blocks are evaluated in a separate context.
• The following code evaluates to false:
• |outerContext answer|
• outerContext := thisContext.
• (1 to: 1) do: [:i | answer := thisContext ==
outerContext].
• ^answer
• But the following evaluates to true because: to:do: is an
inlined block
• |outerContext answer|
• outerContext := thisContext.
• 1 to: 1 do: [:i | answer := thisContext == outerContext].
• ^answer
Contexts
S.Ducasse 11
• Instead of:
• |maxNumber|
• maxNumber := 0.
• #(1 2 43 56 2 49 3 2 0 ) do: [:each| maxNumber :=
each max: maxNumber].
• ^maxNumber
• Write
• #(1 2 43 56 2 49 3 2 0 ) inject: 0 into: [:maxNumber
:ele| maxNumber max: ele]
• no need for a temporary variable
• full block becomes a clean block
inject:into:
S.Ducasse 12
• str1 , str2 creates a new structure in which str1 and
str2 elements are stored
SequenceableCollection>>, aSequenceableCollection
"Answer a copy of the receiver concatenated with the argument,
a SequenceableCollection."
^self copyReplaceFrom: self size + 1
to: self size
with: aSequenceableCollection
SequenceableCollection>>copyReplaceFrom: start to: stop with:
replacementCollection
"Answer a copy of the receiver satisfying the following conditions:
.."
About String Concatenation
S.Ducasse 13
• Suppose that we want to concatenate a pretty long list
of strings, for example the keys of the Smalltalk
dictionary.
• |bigString|
• bigString := String new.
• Smalltalk keys do: [:aString | bigString := bigString, aString].
• Here the assignment of bigString leads to a Full Block
• We can suppress the assignment like that and thus
obtain a clean block
• |aStream|
• aStream:= WriteStream on: String new.
• Smalltalk keys do: [:aString | aStream nextPutAll: aString].
Streams, Blocks, and Optimization
S.Ducasse 14
• inject:into: allows us to suppress the reference to
variables that are outside the block and to obtain a
clean block.
• |aStream|
• aStream:= WriteStream on: String new.
• Smalltalk keys inject: aStream
into: [:cumul :aString| cumul nextPutAll: aString. cumul]
Streams, Blocks, and Optimization (ii)
S.Ducasse 15
• InstanceVariables:
• method <CompiledBlock>
• outerContext <Context | nil>
• copiedValues <Object | Array | nil>
• "Clean" closure with no references to anything from outer scopes.A clean
closure has outerContext = nil and copiedValues = empty Array.
• "Copying" closure that copies immutable values from outer scopes when the
closure is created.A copying closure has outerContext = nil and copiedValues =
Object or Array.
• "Full" closure that retains a reference to the next outer scope.A full closure has
outerContext ~= nil and copiedValues = nil.
• As an optimization, copiedValues holds the single copied value if there is exactly
one, or an Array of values if there is more than one. Note that if there is a
single copied value, the value being copied can be nil, so testing for nil in
copiedValues is not a reliable means of classifying closures.The way to check
whether a closure has copied values is to ask its method whether
numCopiedValues > 0.
BlockClosure Class Comments
S.Ducasse 16
• Now if we use a stream for the Smalltalk keys we can
avoid an iteration method.With whileFalse: that is
inlined the block itself will be inlined.
• |aReadStream aWriteStream|
• aReadStream := ReadStream on: Smalltalk keys asArray.
• aWriteStream := WriteStream on: String new.
• [aReadStream atEnd] whileFalse: [aWriteStream nextPutAll: a ReadStream
next].
• OptimizationYes, but Readibility First
Streams, Blocks, and Optimization (iii)
S.Ducasse 17
Summary
Blocks
Try to define clean blocks

Stoop 300-block optimizationinvw

  • 1.
    S.Ducasse 1 QuickTime™ andaTIFF (Uncompressed) decompressorare needed to see this picture. Stéphane Ducasse Stephane.Ducasse@univ-savoie.fr http://www.listic.univ-savoie.fr/~ducasse/ Blocks and Optimization in VW
  • 2.
    S.Ducasse 2 License: CC-Attribution-ShareAlike2.0 http://creativecommons.org/licenses/by-sa/2.0/
  • 3.
    S.Ducasse 3 [ :x:y | |tmp| ...] value value: value: value: value: value: value: valueWithArguments: •InVisualWorks there are four types of blocks: • Full Blocks • Copying Blocks • Clean Blocks • Inlined Blocks •The programmer does not have to explicitly mention. •Inferred by the compiler. However, knowing the subtle differences allows the programmer to write more efficient code. Blocks and Optimization inVW
  • 4.
    S.Ducasse 4 • Readand assign temporary variables. • Block containing explicit return ^. • Compiled in a BlockClosure. • Evaluation by the creation of an explicit MethodContext or BlockContext object instead of using a pseudo-object contained in the stack. • Most costly • Instead of: • m1: arg1 m1: arg1 • arg1 isNil ^ arg1 isNil • ifTrue: [^ 1] ifTrue: [1] • ifTrue: [^ 2] ifTrue: [2] Full Blocks
  • 5.
    S.Ducasse 5 • Readtemporary variables but do not assign them. • No explicit return. • Access instance variables of self and assign them. • Not compiled into a BlockClosure. • They are compiled by copying every access into the block, thus avoiding explicit references to a context where the copied variables appear. • Their arguments and temporaries are merged into the enclosing method’s context as “compiler-generated temporaries”. Copying Blocks
  • 6.
    S.Ducasse 6 • Containonly reference block temporary variables or global variables. • No reference to self or to instance variables. • nodes do: [:each | each name = #stef] • nodes select: [:each | each isLocal] Clean Blocks
  • 7.
    S.Ducasse 7 • Codeof certain methods, like whileFalse: ifTrue:, is directly inlined into the code of the calling method. • The literal blocks (without arguments) passed as argument to such methods are also inlined in the byte-code of the calling method. • Inlined methods are whileTrue, whileTrue:, whileFalse, whileFalse:, and: or:, ifTrue:, ifFalse:, ifTrue:ifFalse:, ifFalse:ifTrue:, to:do:, to:do:by: • Look in MessageNode>>transform* methods to see the inlining Inlined Blocks
  • 8.
    S.Ducasse 8 • testInLined •1 to: 5 do: [:x| ] • Compiled into : • | t1 | • t1 := 1. • [t1 <= 5] whileTrue: [t1 := t1 + 1]. • But no BlockClosure is created (look into the byte codes) Inlined Blocks
  • 9.
    S.Ducasse 9 • Insteadof: • |t| • [:x | t := x foo] value: 1. • t := t * 2. • ^t • The reference to t inside the block makes it at least a copying block. • t := makes it full. • With the following we have a clean block. • |t| • t := [:x | x foo] value:1. • t := t * 2. • ^t From Full to Copy
  • 10.
    S.Ducasse 10 • Fullblocks are evaluated in a separate context. • The following code evaluates to false: • |outerContext answer| • outerContext := thisContext. • (1 to: 1) do: [:i | answer := thisContext == outerContext]. • ^answer • But the following evaluates to true because: to:do: is an inlined block • |outerContext answer| • outerContext := thisContext. • 1 to: 1 do: [:i | answer := thisContext == outerContext]. • ^answer Contexts
  • 11.
    S.Ducasse 11 • Insteadof: • |maxNumber| • maxNumber := 0. • #(1 2 43 56 2 49 3 2 0 ) do: [:each| maxNumber := each max: maxNumber]. • ^maxNumber • Write • #(1 2 43 56 2 49 3 2 0 ) inject: 0 into: [:maxNumber :ele| maxNumber max: ele] • no need for a temporary variable • full block becomes a clean block inject:into:
  • 12.
    S.Ducasse 12 • str1, str2 creates a new structure in which str1 and str2 elements are stored SequenceableCollection>>, aSequenceableCollection "Answer a copy of the receiver concatenated with the argument, a SequenceableCollection." ^self copyReplaceFrom: self size + 1 to: self size with: aSequenceableCollection SequenceableCollection>>copyReplaceFrom: start to: stop with: replacementCollection "Answer a copy of the receiver satisfying the following conditions: .." About String Concatenation
  • 13.
    S.Ducasse 13 • Supposethat we want to concatenate a pretty long list of strings, for example the keys of the Smalltalk dictionary. • |bigString| • bigString := String new. • Smalltalk keys do: [:aString | bigString := bigString, aString]. • Here the assignment of bigString leads to a Full Block • We can suppress the assignment like that and thus obtain a clean block • |aStream| • aStream:= WriteStream on: String new. • Smalltalk keys do: [:aString | aStream nextPutAll: aString]. Streams, Blocks, and Optimization
  • 14.
    S.Ducasse 14 • inject:into:allows us to suppress the reference to variables that are outside the block and to obtain a clean block. • |aStream| • aStream:= WriteStream on: String new. • Smalltalk keys inject: aStream into: [:cumul :aString| cumul nextPutAll: aString. cumul] Streams, Blocks, and Optimization (ii)
  • 15.
    S.Ducasse 15 • InstanceVariables: •method <CompiledBlock> • outerContext <Context | nil> • copiedValues <Object | Array | nil> • "Clean" closure with no references to anything from outer scopes.A clean closure has outerContext = nil and copiedValues = empty Array. • "Copying" closure that copies immutable values from outer scopes when the closure is created.A copying closure has outerContext = nil and copiedValues = Object or Array. • "Full" closure that retains a reference to the next outer scope.A full closure has outerContext ~= nil and copiedValues = nil. • As an optimization, copiedValues holds the single copied value if there is exactly one, or an Array of values if there is more than one. Note that if there is a single copied value, the value being copied can be nil, so testing for nil in copiedValues is not a reliable means of classifying closures.The way to check whether a closure has copied values is to ask its method whether numCopiedValues > 0. BlockClosure Class Comments
  • 16.
    S.Ducasse 16 • Nowif we use a stream for the Smalltalk keys we can avoid an iteration method.With whileFalse: that is inlined the block itself will be inlined. • |aReadStream aWriteStream| • aReadStream := ReadStream on: Smalltalk keys asArray. • aWriteStream := WriteStream on: String new. • [aReadStream atEnd] whileFalse: [aWriteStream nextPutAll: a ReadStream next]. • OptimizationYes, but Readibility First Streams, Blocks, and Optimization (iii)
  • 17.