[Vm-dev] VM Maker: CogBenchmarks-eem.1.mcz
commits at source.squeak.org
commits at source.squeak.org
Wed Sep 3 17:02:44 UTC 2014
Eliot Miranda uploaded a new version of CogBenchmarks to project VM Maker:
http://source.squeak.org/VMMaker/CogBenchmarks-eem.1.mcz
==================== Summary ====================
Name: CogBenchmarks-eem.1
Author: eem
Time: 3 September 2014, 10:02:40.405 am
UUID: 5bb3e9ae-f915-4f72-94f0-1b09062e7f44
Ancestors:
Move the Cog benchmarks to their own package.
==================== Snapshot ====================
SystemOrganization addCategory: #'CogBenchmarks-DeltaBlue'!
SystemOrganization addCategory: #'CogBenchmarks-Richards'!
SystemOrganization addCategory: #'CogBenchmarks-SMark'!
SystemOrganization addCategory: #'CogBenchmarks-Shootout'!
Object subclass: #DBAbstractConstraint
instanceVariableNames: 'strength'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBAbstractConstraint commentStamp: '<historical>' prior: 0!
I am an abstract class representing a system-maintainable relationship (or "constraint") between a set of variables. I supply a strength instance variable; concrete subclasses provide a means of storing the constrained variables and other information required to represent a constraint.
Instance variables:
strength the strength of this constraint <Strength>!
----- Method: DBAbstractConstraint>>addConstraint (in category 'adding') -----
addConstraint
"Activate this constraint and attempt to satisfy it."
self addToGraph.
DBPlanner current incrementalAdd: self.!
----- Method: DBAbstractConstraint>>addToGraph (in category 'adding') -----
addToGraph
"Add myself to the constraint graph."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>chooseMethod: (in category 'planning') -----
chooseMethod: mark
"Decide if I can be satisfied and record that decision. The output of
the choosen method must not have the given mark and must have a
walkabout strength less than that of this constraint."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>destroyConstraint (in category 'adding') -----
destroyConstraint
"Deactivate this constraint, remove it from the constraint graph,
possibly causing other constraints to be satisfied, and destroy it."
(self isSatisfied) ifTrue: [DBPlanner current incrementalRemove: self].
self removeFromGraph.
self release.!
----- Method: DBAbstractConstraint>>execute (in category 'planning') -----
execute
"Enforce this constraint. Assume that it is satisfied."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>inputsDo: (in category 'planning') -----
inputsDo: aBlock
"Assume that I am satisfied. Evaluate the given block on all my current
input variables."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>inputsKnown: (in category 'planning') -----
inputsKnown: mark
"Assume that I am satisfied. Answer true if all my current inputs are
known. A variable is known if either a) it is 'stay' (i.e. it is a
constant at plan execution time), b) it has the given mark (indicating
that it has been computed by a constraint appearing earlier in the
plan), or c) it is not determined by any constraint."
self inputsDo:
[ :v |
(v mark = mark or: [v stay or: [v determinedBy isNil]]) ifFalse:
[^false]].
^true!
----- Method: DBAbstractConstraint>>isInput (in category 'testing') -----
isInput
"Normal constraints are not input constraints. An input constraint is
one that depends on external state, such as the mouse, the keyboard,
a clock, or some arbitrary piece of imperative code."
^false!
----- Method: DBAbstractConstraint>>isSatisfied (in category 'testing') -----
isSatisfied
"Answer true if this constraint is satisfied in the current solution."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>longPrintOn: (in category 'printing') -----
longPrintOn: aStream
aStream nextPut: $(.
self shortPrintOn: aStream.
aStream space; nextPutAll: self strength printString.
(self isSatisfied)
ifTrue:
[aStream cr; space; space; space.
self inputsDo:
[ :in | aStream nextPutAll: 'v', in printString, ' '].
aStream nextPutAll: '-> '.
aStream nextPutAll: 'v', self output printString]
ifFalse:
[aStream space; nextPutAll: 'UNSATISFIED'].
aStream nextPut: $); cr.!
----- Method: DBAbstractConstraint>>markUnsatisfied (in category 'planning') -----
markUnsatisfied
"Record the fact that I am unsatisfied."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>output (in category 'planning') -----
output
"Answer my current output variable. Raise an error if I am not
currently satisfied."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>printOn: (in category 'printing') -----
printOn: aStream
self shortPrintOn: aStream!
----- Method: DBAbstractConstraint>>recalculate (in category 'planning') -----
recalculate
"Calculate the walkabout strength, the stay flag, and, if it is 'stay',
the value for the current output of this constraint. Assume this
constraint is satisfied."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>removeFromGraph (in category 'adding') -----
removeFromGraph
"Remove myself from the constraint graph."
self subclassResponsibility!
----- Method: DBAbstractConstraint>>satisfy: (in category 'planning') -----
satisfy: mark
"Attempt to find a way to enforce this (still unsatisfied) constraint.
If successful, record the solution, perhaps modifying the current
dataflow graph. Answer the constraint that this constraint overrides,
if there is one, or nil, if there isn't."
| overridden out |
self chooseMethod: mark.
self isSatisfied
ifTrue: "constraint can be satisfied"
["mark inputs to allow cycle detection in addPropagate"
self inputsDo: [ :in | in mark: mark].
out := self output.
overridden := out determinedBy.
overridden ifNotNil: [overridden markUnsatisfied].
out determinedBy: self.
(DBPlanner current addPropagate: self mark: mark) ifFalse:
[self notify:
('Cycle encountered adding:\ ',
self printString,
'\Constraint removed.') withCRs.
^nil].
out mark: mark]
ifFalse: "constraint cannot be satisfied"
[overridden := nil.
(strength sameAs: (DBStrength required)) ifTrue:
[self notify: 'Failed to satisfy a required constraint']].
^ overridden!
----- Method: DBAbstractConstraint>>shortPrintOn: (in category 'printing') -----
shortPrintOn: aStream
aStream nextPutAll: self class name, '(', self printString, ')'.!
----- Method: DBAbstractConstraint>>strength (in category 'accessing') -----
strength
^ strength!
----- Method: DBAbstractConstraint>>strength: (in category 'accessing') -----
strength: anObject
strength := anObject!
DBAbstractConstraint subclass: #DBBinaryConstraint
instanceVariableNames: 'direction v1 v2'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBBinaryConstraint commentStamp: '<historical>' prior: 0!
I am an abstract superclass for constraints having two possible output variables.
Instance variables:
v1, v2 possible output variables <Variable>
direction one of:
#forward (v2 is output)
#backward ( v1 is output)
nil (not satisfied)!
----- Method: DBBinaryConstraint class>>var:var:strength: (in category 'instance creation') -----
var: variable1 var: variable2 strength: strengthSymbol
"Install a constraint with the given strength equating the given
variables."
^(self new) var: variable1 var: variable2 strength: strengthSymbol!
----- Method: DBBinaryConstraint>>addToGraph (in category 'adding') -----
addToGraph
"Add myself to the constraint graph."
v1 addConstraint: self.
v2 addConstraint: self.
direction := nil.!
----- Method: DBBinaryConstraint>>chooseMethod: (in category 'planning') -----
chooseMethod: mark
"Decide if I can be satisfied and which way I should flow based on the relative strength of the variables I relate, and record that decision."
v1 mark == mark ifTrue: "forward or nothing"
[ ^ direction := ((v2 mark ~= mark) and: [strength stronger: v2 walkStrength])
ifTrue: [ #forward ]
ifFalse: [ nil ] ].
v2 mark == mark ifTrue: "backward or nothing"
[ ^ direction := ((v1 mark ~= mark) and: [strength stronger: v1 walkStrength])
ifTrue: [ #backward ]
ifFalse: [ nil ] ].
"if we get here, neither variable is marked, so we have choice"
(v1 walkStrength weaker: v2 walkStrength)
ifTrue:
[ ^ direction := (strength stronger: v1 walkStrength)
ifTrue: [ #backward ]
ifFalse: [ nil ]]
ifFalse:
[ ^ direction := (strength stronger: v2 walkStrength)
ifTrue: [ #forward ]
ifFalse: [ nil ]]. !
----- Method: DBBinaryConstraint>>execute (in category 'planning') -----
execute
"Enforce this constraint. Assume that it is satisfied."
self subclassResponsibility!
----- Method: DBBinaryConstraint>>inputsDo: (in category 'planning') -----
inputsDo: aBlock
"Evaluate the given block on my current input variable."
direction == #forward
ifTrue: [ aBlock value: v1 ]
ifFalse: [ aBlock value: v2 ].!
----- Method: DBBinaryConstraint>>isSatisfied (in category 'testing') -----
isSatisfied
"Answer true if this constraint is satisfied in the current solution."
^ direction notNil!
----- Method: DBBinaryConstraint>>markUnsatisfied (in category 'planning') -----
markUnsatisfied
"Record the fact that I am unsatisfied."
direction := nil.!
----- Method: DBBinaryConstraint>>output (in category 'planning') -----
output
"Answer my current output variable."
^ direction == #forward
ifTrue: [ v2 ]
ifFalse: [ v1 ]!
----- Method: DBBinaryConstraint>>recalculate (in category 'planning') -----
recalculate
"Calculate the walkabout strength, the stay flag, and, if it is 'stay',
the value for the current output of this constraint. Assume this
constraint is satisfied."
| in out |
direction == #forward
ifTrue: [in := v1. out := v2]
ifFalse: [in := v2. out := v1].
out walkStrength: (strength weakest: in walkStrength).
out stay: in stay.
out stay ifTrue: [ self execute ]. "stay optimization"!
----- Method: DBBinaryConstraint>>removeFromGraph (in category 'adding') -----
removeFromGraph
"Remove myself from the constraint graph."
v1 ifNotNil: [v1 removeConstraint: self].
v2 ifNotNil: [v2 removeConstraint: self].
direction := nil.!
----- Method: DBBinaryConstraint>>var:var:strength: (in category 'initialize') -----
var: variable1 var: variable2 strength: strengthSymbol
"Initialize myself with the given variables and strength."
strength := DBStrength of: strengthSymbol.
v1 := variable1.
v2 := variable2.
direction := nil.
self addConstraint.!
DBBinaryConstraint subclass: #DBEqualityConstraint
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBEqualityConstraint commentStamp: '<historical>' prior: 0!
I constrain two variables to have the same value: "v1 = v2".!
----- Method: DBEqualityConstraint>>execute (in category 'planning') -----
execute
"Enforce this constraint. Assume that it is satisfied."
direction == #forward
ifTrue: [v2 value: v1 value]
ifFalse: [v1 value: v2 value].!
DBBinaryConstraint subclass: #DBScaleConstraint
instanceVariableNames: 'offset scale'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBScaleConstraint commentStamp: '<historical>' prior: 0!
I relate two variables by the linear scaling relationship:
"v2 = (v1 * scale) + offset". Either v1 or v2 may be changed to maintain this
relationship but the scale factor and offset are considered read-only.
Instance variables:
scale scale factor input variable <Variable>
offset offset input variable <Variable>!
----- Method: DBScaleConstraint class>>var:var:var:var:strength: (in category 'instance creation') -----
var: src var: scale var: offset var: dst strength: strengthSymbol
"Install a scale constraint with the given strength on the given variables."
^(self new) src: src scale: scale offset: offset dst: dst strength: strengthSymbol!
----- Method: DBScaleConstraint>>addToGraph (in category 'adding') -----
addToGraph
"Add myself to the constraint graph."
super addToGraph.
scale addConstraint: self.
offset addConstraint: self.
!
----- Method: DBScaleConstraint>>execute (in category 'planning') -----
execute
"Enforce this constraint. Assume that it is satisfied."
direction == #forward
ifTrue: [v2 value: v1 value * scale value + offset value]
ifFalse: [v1 value: v2 value - offset value // scale value].!
----- Method: DBScaleConstraint>>inputsDo: (in category 'planning') -----
inputsDo: aBlock
"Evaluate the given block on my current input variable."
direction == #forward
ifTrue: [ aBlock
value: v1;
value: scale;
value: offset]
ifFalse: [ aBlock
value: v2;
value: scale;
value: offset].!
----- Method: DBScaleConstraint>>recalculate (in category 'planning') -----
recalculate
"Calculate the walkabout strength, the stay flag, and, if it is 'stay', the value for the current output of this constraint. Assume this constraint is satisfied."
| in out |
direction == #forward
ifTrue: [in := v1. out := v2]
ifFalse: [out := v1. in := v2].
out walkStrength: (strength weakest: in walkStrength).
out stay: (in stay and: [scale stay and: [offset stay]]).
out stay ifTrue: [self execute]. "stay optimization"!
----- Method: DBScaleConstraint>>removeFromGraph (in category 'adding') -----
removeFromGraph
"Remove myself from the constraint graph."
super removeFromGraph.
scale ifNotNil: [scale removeConstraint: self].
offset ifNotNil: [offset removeConstraint: self].
!
----- Method: DBScaleConstraint>>src:scale:offset:dst:strength: (in category 'initialize') -----
src: srcVar scale: scaleVar offset: offsetVar dst: dstVar strength: strengthSymbol
"Initialize myself with the given variables and strength."
strength := DBStrength of: strengthSymbol.
v1 := srcVar.
v2 := dstVar.
scale := scaleVar.
offset := offsetVar.
direction := nil.
self addConstraint.!
DBAbstractConstraint subclass: #DBUnaryConstraint
instanceVariableNames: 'output satisfied'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBUnaryConstraint commentStamp: '<historical>' prior: 0!
I am an abstract superclass for constraints having a single possible output variable.
Instance variables:
output possible output variable <Variable>
satisfied true if I am currently satisfied <Boolean>!
DBUnaryConstraint subclass: #DBEditConstraint
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBEditConstraint commentStamp: '<historical>' prior: 0!
I mark variables that should, with some level of preference, stay the same. I have one method with zero inputs and one output, which does nothing. Planners may exploit the fact that, if I am satisfied, my output will not change during plan execution. This is called "stay optimization."!
----- Method: DBEditConstraint>>execute (in category 'planning') -----
execute
"Edit constraints do nothing."!
----- Method: DBEditConstraint>>isInput (in category 'testing') -----
isInput
"I indicate that a variable is to be changed by imperative code."
^true!
DBUnaryConstraint subclass: #DBStayConstraint
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBStayConstraint commentStamp: '<historical>' prior: 0!
I mark variables that should, with some level of preference, stay the same.
I have one method with zero inputs and one output, which does nothing. Planners
may exploit the fact that, if I am satisfied, my output will not change during
plan execution. This is called "stay optimization."!
----- Method: DBStayConstraint>>execute (in category 'planning') -----
execute
"Stay constraints do nothing."!
----- Method: DBUnaryConstraint class>>var:strength: (in category 'instance creation') -----
var: aVariable strength: strengthSymbol
"Install an edit constraint with the given strength on the given
variable."
^(self new) var: aVariable strength: strengthSymbol!
----- Method: DBUnaryConstraint>>addToGraph (in category 'adding') -----
addToGraph
"Add myself to the constraint graph."
output addConstraint: self.
satisfied := false.!
----- Method: DBUnaryConstraint>>chooseMethod: (in category 'planning') -----
chooseMethod: mark
"Decide if I can be satisfied and record that decision."
satisfied :=
output mark ~= mark and:
[strength stronger: output walkStrength].!
----- Method: DBUnaryConstraint>>execute (in category 'planning') -----
execute
"Enforce this constraint. Assume that it is satisfied."
self subclassResponsibility!
----- Method: DBUnaryConstraint>>inputsDo: (in category 'planning') -----
inputsDo: aBlock
"I have no input variables."!
----- Method: DBUnaryConstraint>>isSatisfied (in category 'testing') -----
isSatisfied
"Answer true if this constraint is satisfied in the current solution."
^satisfied!
----- Method: DBUnaryConstraint>>markUnsatisfied (in category 'planning') -----
markUnsatisfied
"Record the fact that I am unsatisfied."
satisfied := false.!
----- Method: DBUnaryConstraint>>output (in category 'planning') -----
output
"Answer my current output variable."
^ output!
----- Method: DBUnaryConstraint>>recalculate (in category 'planning') -----
recalculate
"Calculate the walkabout strength, the stay flag, and, if it is 'stay',
the value for the current output of this constraint. Assume this
constraint is satisfied."
output walkStrength: strength.
output stay: self isInput not.
output stay ifTrue: [self execute]. "stay optimization"!
----- Method: DBUnaryConstraint>>removeFromGraph (in category 'adding') -----
removeFromGraph
"Remove myself from the constraint graph."
output ifNotNil: [ :out | out removeConstraint: self].
satisfied := false.!
----- Method: DBUnaryConstraint>>var:strength: (in category 'initialize') -----
var: aVariable strength: strengthSymbol
"Initialize myself with the given variable and strength."
strength := DBStrength of: strengthSymbol.
output := aVariable.
satisfied := false.
self addConstraint.!
Object subclass: #DBPlanner
instanceVariableNames: 'currentMark'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
DBPlanner class
instanceVariableNames: 'currentPlanner'!
!DBPlanner commentStamp: '<historical>' prior: 0!
This benchmark is an implementation of the DeltaBlue Constraint Solver described in `The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver'', by Bjorn N. Freeman-Benson and John Maloney, Communications of the ACM, January 1990 (also as University of Washington TR 89-08-06).
!
DBPlanner class
instanceVariableNames: 'currentPlanner'!
----- Method: DBPlanner class>>current (in category 'accessing') -----
current
^ currentPlanner!
----- Method: DBPlanner class>>new (in category 'instance creation') -----
new
^ currentPlanner := super new!
----- Method: DBPlanner>>addConstraintsConsuming:to: (in category 'private') -----
addConstraintsConsuming: v to: aCollection
| determiningC |
determiningC := v determinedBy.
v constraints do:
[ :c |
(c == determiningC or: [c isSatisfied not]) ifFalse:
[aCollection add: c]].!
----- Method: DBPlanner>>addPropagate:mark: (in category 'private') -----
addPropagate: c mark: mark
"Recompute the walkabout strengths and stay flags of all variables
downstream of the given constraint and recompute the actual values
of all variables whose stay flag is true. If a cycle is detected,
remove the given constraint and answer false. Otherwise, answer true.
Details: Cycles are detected when a marked variable is encountered
downstream of the given constraint. The sender is assumed to have
marked the inputs of the given constraint with the given mark. Thus,
encountering a marked node downstream of the output constraint means
that there is a path from the constraint's output to one of its
inputs."
| todo d |
todo := OrderedCollection with: c.
[todo isEmpty] whileFalse:
[d := todo removeFirst.
(d output mark = mark) ifTrue:
[self incrementalRemove: c.
^ false].
d recalculate.
self addConstraintsConsuming: d output to: todo].
^ true!
----- Method: DBPlanner>>changeVar:newValue: (in category 'private') -----
changeVar: aVariable newValue: newValue
| editConstraint plan |
editConstraint := DBEditConstraint var: aVariable strength: #preferred.
plan := self extractPlanFromConstraints: (Array with: editConstraint).
10 timesRepeat: [
aVariable value: newValue.
plan execute].
editConstraint destroyConstraint.!
----- Method: DBPlanner>>constraintsConsuming:do: (in category 'private') -----
constraintsConsuming: v do: aBlock
| determiningC |
determiningC := v determinedBy.
v constraints do:
[ :c |
(c == determiningC or: [c isSatisfied not]) ifFalse:
[aBlock value: c]].!
----- Method: DBPlanner>>extractPlanFromConstraints: (in category 'planning') -----
extractPlanFromConstraints: constraints
"Extract a plan for resatisfaction starting from the outputs of the
given constraints, usually a set of input constraints."
| sources |
sources := OrderedCollection new.
constraints do:
[: c | (c isInput and: [c isSatisfied]) ifTrue: [sources add: c]].
^self makePlan: sources!
----- Method: DBPlanner>>extractPlanFromVariables: (in category 'planning') -----
extractPlanFromVariables: variables
"Extract a plan from the dataflow graph having the given variables. It
is assumed that the given set of variables is complete, or at least
that it contains all the input variables."
| sources |
sources := OrderedCollection new.
variables do:
[: v |
(v constraints) do:
[: c | (c isInput and: [c isSatisfied]) ifTrue: [sources add: c]]].
^self makePlan: sources!
----- Method: DBPlanner>>incrementalAdd: (in category 'adding') -----
incrementalAdd: c
"Attempt to satisfy the given constraint and, if successful,
incrementally update the dataflow graph.
Details: If satifying the constraint is successful, it may override a
weaker constraint on its output. The algorithm attempts to resatisfy
that constraint using some other method. This process is repeated
until either a) it reaches a variable that was not previously
determined by any constraint or b) it reaches a constraint that
is too weak to be satisfied using any of its methods. The variables
of constraints that have been processed are marked with a unique mark
value so that we know where we've been. This allows the algorithm to
avoid getting into an infinite loop even if the constraint graph has
an inadvertent cycle."
| mark overridden |
mark := self newMark.
overridden := c satisfy: mark.
[overridden isNil] whileFalse:
[overridden := overridden satisfy: mark].!
----- Method: DBPlanner>>incrementalRemove: (in category 'adding') -----
incrementalRemove: c
"Entry point for retracting a constraint. Remove the given constraint,
which should be satisfied, and incrementally update the dataflow
graph.
Details: Retracting the given constraint may allow some currently
unsatisfiable downstream constraint be satisfied. We thus collect a
list of unsatisfied downstream constraints and attempt to satisfy
each one in turn. This list is sorted by constraint strength,
strongest first, as a heuristic for avoiding unnecessarily adding
and then overriding weak constraints."
| out unsatisfied |
out := c output.
c markUnsatisfied.
c removeFromGraph.
unsatisfied := self removePropagateFrom: out.
unsatisfied do: [: u | self incrementalAdd: u].!
----- Method: DBPlanner>>initialize (in category 'initialize') -----
initialize
super initialize.
currentMark := 1.!
----- Method: DBPlanner>>makePlan: (in category 'planning') -----
makePlan: sources
"Extract a plan for resatisfaction starting from the given satisfied
source constraints, usually a set of input constraints. This method
assumes that stay optimization is desired; the plan will contain only
constraints whose output variables are not stay. Constraints that do
no computation, such as stay and edit constraints, are not included
in the plan.
Details: The outputs of a constraint are marked when it is added to
the plan under construction. A constraint may be appended to the plan
when all its input variables are known. A variable is known if either
a) the variable is marked (indicating that has been computed by a
constraint appearing earlier in the plan), b) the variable is 'stay'
(i.e. it is a constant at plan execution time), or c) the variable
is not determined by any constraint. The last provision is for past
states of history variables, which are not stay but which are also
not computed by any constraint."
| mark plan todo c |
mark := self newMark.
plan := DBPlan new.
todo := sources.
[todo isEmpty] whileFalse:
[c := todo removeFirst.
((c output mark ~= mark) and: "not in plan already and..."
[c inputsKnown: mark]) ifTrue: "eligible for inclusion"
[plan addLast: c.
c output mark: mark.
self addConstraintsConsuming: c output to: todo]].
^ plan!
----- Method: DBPlanner>>newMark (in category 'private') -----
newMark
"Select a previously unused mark value.
Details: We just keep incrementing. If necessary, the counter will
turn into a LargePositiveInteger. In that case, it will be a bit
slower to compute the next mark but the algorithms will all behave
correctly. We reserve the value '0' to mean 'unmarked'. Thus, this
generator starts at '1' and will never produce '0' as a mark value."
^currentMark := currentMark + 1!
----- Method: DBPlanner>>propagateFrom: (in category 'planning') -----
propagateFrom: v
"The given variable has changed. Propagate new values downstream."
| todo c |
todo := OrderedCollection new.
self addConstraintsConsuming: v to: todo.
[todo isEmpty] whileFalse:
[c := todo removeFirst.
c execute.
self addConstraintsConsuming: c output to: todo].!
----- Method: DBPlanner>>removePropagateFrom: (in category 'private') -----
removePropagateFrom: out
"Update the walkabout strengths and stay flags of all variables
downstream of the given constraint. Answer a collection of unsatisfied
constraints sorted in order of decreasing strength."
| unsatisfied todo v |
unsatisfied := SortedCollection sortBlock:
[ :c1 :c2 | c1 strength stronger: c2 strength].
out determinedBy: nil.
out walkStrength: DBStrength absoluteWeakest.
out stay: true.
todo := OrderedCollection with: out.
[todo isEmpty] whileFalse:
[v := todo removeFirst.
v constraints do:
[ :c | c isSatisfied ifFalse: [unsatisfied add: c]].
self constraintsConsuming: v do:
[ :c |
c recalculate.
todo add: c output]].
^ unsatisfied!
Object subclass: #DBStrength
instanceVariableNames: 'symbolicValue arithmeticValue'
classVariableNames: 'AbsoluteStrongest AbsoluteWeakest Required StrengthConstants StrengthTable'
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBStrength commentStamp: '<historical>' prior: 0!
Strengths are used to measure the relative importance of constraints. The hierarchy of available strengths is determined by the class variable StrengthTable (see my class initialization method). Because Strengths are invariant, references to Strength instances are shared (i.e. all references to
"Strength of: #required" point to a single, shared instance). New strengths may be inserted in the strength hierarchy without disrupting current constraints.
Instance variables:
symbolicValue symbolic strength name (e.g. #required) <Symbol>
arithmeticValue index of the constraint in the hierarchy, used for comparisons <Number>!
----- Method: DBStrength class>>absoluteStrongest (in category 'constants') -----
absoluteStrongest
^AbsoluteStrongest!
----- Method: DBStrength class>>absoluteWeakest (in category 'constants') -----
absoluteWeakest
^AbsoluteWeakest!
----- Method: DBStrength class>>initialize (in category 'initialize') -----
initialize
"Initialize the symbolic strength table. Fix the internally caches
values of all existing instances."
"Strength initialize"
StrengthTable := Dictionary new.
StrengthTable at: #absoluteStrongest put: -10000.
StrengthTable at: #required put: -800.
StrengthTable at: #strongPreferred put: -600.
StrengthTable at: #preferred put: -400.
StrengthTable at: #strongDefault put: -200.
StrengthTable at: #default put: 0.
StrengthTable at: #weakDefault put: 500.
StrengthTable at: #absoluteWeakest put: 10000.
StrengthConstants := Dictionary new.
StrengthTable keys do:
[: strengthSymbol |
StrengthConstants
at: strengthSymbol
put: ((super new) initializeWith: strengthSymbol)].
AbsoluteStrongest := DBStrength of: #absoluteStrongest.
AbsoluteWeakest := DBStrength of: #absoluteWeakest.
Required := DBStrength of: #required.!
----- Method: DBStrength class>>of: (in category 'instance creation') -----
of: aSymbol
"Answer an instance with the specified strength."
^ StrengthConstants at: aSymbol!
----- Method: DBStrength class>>required (in category 'constants') -----
required
^Required!
----- Method: DBStrength>>arithmeticValue (in category 'private') -----
arithmeticValue
"Answer my arithmetic value. Used for comparisons. Note that
STRONGER constraints have SMALLER arithmetic values."
^arithmeticValue!
----- Method: DBStrength>>initializeWith: (in category 'private') -----
initializeWith: symVal
"Record my symbolic value and reset my arithmetic value."
symbolicValue := symVal.
arithmeticValue := StrengthTable at: symbolicValue.!
----- Method: DBStrength>>printOn: (in category 'printing') -----
printOn: aStream
"Append a string which represents my strength onto aStream."
aStream nextPutAll: '%', symbolicValue, '%'.!
----- Method: DBStrength>>sameAs: (in category 'comparing') -----
sameAs: aStrength
"Answer true if I am the same strength as the given Strength."
^ arithmeticValue = aStrength arithmeticValue!
----- Method: DBStrength>>stronger: (in category 'comparing') -----
stronger: aStrength
"Answer true if I am stronger than the given Strength."
^ arithmeticValue < aStrength arithmeticValue!
----- Method: DBStrength>>strongest: (in category 'max / min') -----
strongest: aStrength
"Answer the stronger of myself and aStrength."
^ (aStrength stronger: self)
ifTrue: [aStrength]
ifFalse: [self]!
----- Method: DBStrength>>weaker: (in category 'comparing') -----
weaker: aStrength
"Answer true if I am weaker than the given Strength."
^ arithmeticValue > aStrength arithmeticValue!
----- Method: DBStrength>>weakest: (in category 'max / min') -----
weakest: aStrength
"Answer the weaker of myself and aStrength."
^ (aStrength weaker: self)
ifTrue: [aStrength]
ifFalse: [self].!
Object subclass: #DBVariable
instanceVariableNames: 'constraints determinedBy mark stay value walkStrength'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBVariable commentStamp: '<historical>' prior: 0!
I represent a constrained variable. In addition to my value, I maintain the structure of the constraint graph, the current dataflow graph, and various parameters of interest to the DeltaBlue incremental constraint solver.
Instance variables:
value my value; changed by constraints, read by client <Object>
constraints normal constraints that reference me <Array of Constraint>
determinedBy the constraint that currently determines
my value (or nil if there isn''t one) <Constraint>
walkStrength my walkabout strength <Strength>
stay true if I am a planning-time constant <Boolean>
mark used by the planner to mark constraints <Number>!
----- Method: DBVariable class>>value: (in category 'instance creation') -----
value: aValue
^self new value: aValue!
----- Method: DBVariable>>addConstraint: (in category 'accessing') -----
addConstraint: aConstraint
"Add the given constraint to the set of all constraints that refer
to me."
constraints add: aConstraint.!
----- Method: DBVariable>>constraints (in category 'accessing') -----
constraints
^ constraints!
----- Method: DBVariable>>determinedBy (in category 'accessing') -----
determinedBy
^ determinedBy!
----- Method: DBVariable>>determinedBy: (in category 'accessing') -----
determinedBy: anObject
determinedBy := anObject!
----- Method: DBVariable>>initialize (in category 'initialize') -----
initialize
super initialize.
value := 0.
constraints := OrderedCollection new: 2.
determinedBy := nil.
walkStrength := DBStrength absoluteWeakest.
stay := true.
mark := 0.!
----- Method: DBVariable>>longPrintOn: (in category 'printing') -----
longPrintOn: aStream
self shortPrintOn: aStream.
aStream nextPutAll: ' Constraints: '.
(constraints isEmpty)
ifTrue: [aStream cr; tab; nextPutAll: 'none']
ifFalse:
[constraints do:
[: c | aStream cr; tab. c shortPrintOn: aStream]].
(determinedBy isNil) ifFalse:
[aStream cr; nextPutAll: ' Determined by: '.
aStream cr; tab. determinedBy shortPrintOn: aStream].
aStream cr.!
----- Method: DBVariable>>mark (in category 'accessing') -----
mark
^ mark!
----- Method: DBVariable>>mark: (in category 'accessing') -----
mark: anObject
mark := anObject!
----- Method: DBVariable>>printOn: (in category 'printing') -----
printOn: aStream
self shortPrintOn: aStream!
----- Method: DBVariable>>removeConstraint: (in category 'accessing') -----
removeConstraint: c
"Remove all traces of c from this variable."
constraints remove: c ifAbsent: [].
determinedBy == c ifTrue: [determinedBy := nil].!
----- Method: DBVariable>>setValue: (in category 'update') -----
setValue: aValue
"Attempt to assign the given value to me using a strength of
#preferred."
self setValue: aValue strength: #preferred.!
----- Method: DBVariable>>setValue:strength: (in category 'update') -----
setValue: aValue strength: strengthSymbol
"Attempt to assign the given value to me using the given strength."
| editConstraint |
editConstraint := DBEditConstraint var: self strength: strengthSymbol.
(editConstraint isSatisfied) ifTrue:
[self value: aValue.
DBPlanner propagateFrom: self].
editConstraint destroyConstraint.!
----- Method: DBVariable>>shortPrintOn: (in category 'printing') -----
shortPrintOn: aStream
aStream nextPutAll: 'V(', self printString, ', '.
aStream nextPutAll: walkStrength printString, ', '.
(stay isNil) ifFalse:
[aStream nextPutAll: (stay ifTrue: ['stay, '] ifFalse: ['changing, '])].
aStream nextPutAll: value printString.
aStream nextPutAll: ')'.
aStream cr.!
----- Method: DBVariable>>stay (in category 'accessing') -----
stay
^ stay!
----- Method: DBVariable>>stay: (in category 'accessing') -----
stay: anObject
stay := anObject!
----- Method: DBVariable>>value (in category 'accessing') -----
value
^ value!
----- Method: DBVariable>>value: (in category 'accessing') -----
value: anObject
value := anObject!
----- Method: DBVariable>>walkStrength (in category 'accessing') -----
walkStrength
^ walkStrength!
----- Method: DBVariable>>walkStrength: (in category 'accessing') -----
walkStrength: anObject
walkStrength := anObject!
Object subclass: #RichObject
instanceVariableNames: ''
classVariableNames: 'DeviceA DeviceB DevicePacketKind HandlerA HandlerB Idler WorkPacketKind Worker'
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichObject commentStamp: '<historical>' prior: 0!
This class mostly adds some constants that are used in the Richards benchmarks.!
RichObject subclass: #RichDeviceTaskDataRecord
instanceVariableNames: 'pending'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichDeviceTaskDataRecord commentStamp: '<historical>' prior: 0!
A task that suspends itself after each time it has been run to simulate waiting for data from an external device.
!
----- Method: RichDeviceTaskDataRecord>>pending (in category 'accessing') -----
pending
^ pending!
----- Method: RichDeviceTaskDataRecord>>pending: (in category 'accessing') -----
pending: anObject
pending := anObject!
RichObject subclass: #RichHandlerTaskDataRecord
instanceVariableNames: 'workIn deviceIn'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichHandlerTaskDataRecord commentStamp: '<historical>' prior: 0!
A task that manipulates work packets and then suspends itself.!
----- Method: RichHandlerTaskDataRecord>>deviceIn (in category 'accessing') -----
deviceIn
^ deviceIn!
----- Method: RichHandlerTaskDataRecord>>deviceIn: (in category 'accessing') -----
deviceIn: anObject
deviceIn := anObject!
----- Method: RichHandlerTaskDataRecord>>deviceInAdd: (in category 'accessing') -----
deviceInAdd: packet
deviceIn := self append: packet head: deviceIn!
----- Method: RichHandlerTaskDataRecord>>workIn (in category 'accessing') -----
workIn
^ workIn!
----- Method: RichHandlerTaskDataRecord>>workIn: (in category 'accessing') -----
workIn: anObject
workIn := anObject!
----- Method: RichHandlerTaskDataRecord>>workInAdd: (in category 'accessing') -----
workInAdd: packet
workIn := self append: packet head: workIn!
RichObject subclass: #RichIdleTaskDataRecord
instanceVariableNames: 'control count'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichIdleTaskDataRecord commentStamp: '<historical>' prior: 0!
An idle task doesn't do any work itself but cycles control between the two device tasks.!
----- Method: RichIdleTaskDataRecord>>control (in category 'accessing') -----
control
^ control!
----- Method: RichIdleTaskDataRecord>>control: (in category 'accessing') -----
control: anObject
control := anObject!
----- Method: RichIdleTaskDataRecord>>count (in category 'accessing') -----
count
^ count!
----- Method: RichIdleTaskDataRecord>>count: (in category 'accessing') -----
count: anObject
count := anObject!
----- Method: RichIdleTaskDataRecord>>initialize (in category 'initialize') -----
initialize
control := 1.
count := 10000!
----- Method: RichObject class>>initialize (in category 'initialize') -----
initialize
super initialize.
DeviceA := 5.
DeviceB := 6.
DevicePacketKind := 1.
HandlerA := 3.
HandlerB := 4.
Idler := 1.
Worker := 2.
WorkPacketKind := 2!
----- Method: RichObject>>append:head: (in category 'utilities') -----
append: packet head: queueHead
| mouse link |
packet link: nil.
queueHead ifNil: [ ^ packet ].
mouse := queueHead.
[ (link := mouse link) isNil]
whileFalse: [ mouse := link ].
mouse link: packet.
^ queueHead!
RichObject subclass: #RichPacket
instanceVariableNames: 'data datum identity kind link'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichPacket commentStamp: '<historical>' prior: 0!
A simple package of data that is manipulated by the tasks. The exact layout of the payload data carried by a packet is not importaint, and neither is the nature of the work performed on packets by the tasks. Besides carrying data, packets form linked lists and are hence used both as data and worklists.!
----- Method: RichPacket class>>create:identity:kind: (in category 'instance creation') -----
create: link identity: identity kind: kind
^ self new
link: link
identity: identity
kind: kind!
----- Method: RichPacket>>data (in category 'accessing') -----
data
^ data!
----- Method: RichPacket>>datum (in category 'accessing') -----
datum
^ datum!
----- Method: RichPacket>>datum: (in category 'accessing') -----
datum: anObject
datum := anObject!
----- Method: RichPacket>>identity (in category 'accessing') -----
identity
^ identity!
----- Method: RichPacket>>identity: (in category 'accessing') -----
identity: anObject
identity := anObject!
----- Method: RichPacket>>kind (in category 'accessing') -----
kind
^ kind!
----- Method: RichPacket>>link (in category 'accessing') -----
link
^ link!
----- Method: RichPacket>>link: (in category 'accessing') -----
link: anObject
link := anObject!
----- Method: RichPacket>>link:identity:kind: (in category 'initialize') -----
link: aLink identity: anIdentity kind: aKind
link := aLink.
identity := anIdentity.
kind := aKind.
datum := 1.
data := ByteArray new: 4!
RichObject subclass: #RichRunner
instanceVariableNames: 'taskList currentTask currentTaskIdentity taskTable queuePacketCount holdCount'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichRunner commentStamp: '<historical>' prior: 0!
Richards simulates the task dispatcher of an operating system.!
----- Method: RichRunner class>>start (in category 'starting') -----
start
"RichardsBenchmark start"
^self new start!
----- Method: RichRunner>>createDevice:priority:work:state: (in category 'creation') -----
createDevice: identity priority: priority work: work state: state
| data |
data := RichDeviceTaskDataRecord new.
self
createTask: identity
priority: priority
work: work
state: state
function:
[:work1 :word | | data1 functionWork |
data1 := word.
functionWork := work1.
functionWork
ifNil:
[(functionWork := data1 pending) isNil
ifTrue: [self wait]
ifFalse:
[data1 pending: nil.
self queuePacket: functionWork]]
ifNotNil:
[data1 pending: functionWork.
self holdSelf]]
data: data!
----- Method: RichRunner>>createHandler:priority:work:state: (in category 'creation') -----
createHandler: identity priority: priority work: work state: state
| data |
data := RichHandlerTaskDataRecord new.
self
createTask: identity
priority: priority
work: work
state: state
function:
[:work1 :word | | data1 workPacket count devicePacket |
data1 := word.
work1
ifNotNil: [WorkPacketKind == work1 kind
ifTrue: [data1 workInAdd: work1]
ifFalse: [data1 deviceInAdd: work1]].
(workPacket := data1 workIn)
ifNil: [self wait]
ifNotNil:
[count := workPacket datum.
count > 4
ifTrue:
[data1 workIn: workPacket link.
self queuePacket: workPacket]
ifFalse:
[(devicePacket := data1 deviceIn)
ifNil: [self wait]
ifNotNil:
[data1 deviceIn: devicePacket link.
devicePacket datum: (workPacket data at: count).
workPacket datum: count + 1.
self queuePacket: devicePacket]]]]
data: data!
----- Method: RichRunner>>createIdler:priority:work:state: (in category 'creation') -----
createIdler: identity priority: priority work: work state: state
| data |
data := RichIdleTaskDataRecord new.
self
createTask: identity
priority: priority
work: work
state: state
function:
[:work1 :word | | data1 |
data1 := word.
data1 count: data1 count - 1.
0 = data1 count
ifTrue: [self holdSelf]
ifFalse:
[0 = (data1 control bitAnd: 1)
ifTrue:
[data1 control: data1 control // 2.
self release: DeviceA]
ifFalse:
[data1 control: (data1 control // 2 bitXor: 53256).
self release: DeviceB]]]
data: data!
----- Method: RichRunner>>createPacket:identity:kind: (in category 'creation') -----
createPacket: link identity: identity kind: kind
^ RichPacket
create: link
identity: identity
kind: kind!
----- Method: RichRunner>>createTask:priority:work:state:function:data: (in category 'creation') -----
createTask: identity priority: priority work: work state: state function: aBlock data: data
| t |
t := RichTaskControlBlock
link: taskList
create: identity
priority: priority
initialWorkQueue: work
initialState: state
function: aBlock
privateData: data.
taskList := t.
taskTable at: identity put: t!
----- Method: RichRunner>>createWorker:priority:work:state: (in category 'creation') -----
createWorker: identity priority: priority work: work state: state
| data |
data := RichWorkerTaskDataRecord new.
self
createTask: identity
priority: priority
work: work
state: state
function:
[:work1 :word | | data1 |
data1 := word.
work1
ifNil: [self wait]
ifNotNil:
[data1 destination: (HandlerA = data1 destination
ifTrue: [HandlerB]
ifFalse: [HandlerA]).
work1 identity: data1 destination.
work1 datum: 1.
1 to: 4 do: [ :i |
data1 count: data1 count + 1.
data1 count > 26 ifTrue: [data1 count: 1].
work1 data at: i put: $A asInteger + data1 count - 1].
self queuePacket: work1]]
data: data!
----- Method: RichRunner>>findTask: (in category 'private') -----
findTask: identity
| t |
t := taskTable at: identity.
t ifNil: [self error: 'findTask failed'].
^ t!
----- Method: RichRunner>>holdSelf (in category 'private') -----
holdSelf
holdCount := holdCount + 1.
currentTask taskHolding: true.
^ currentTask link!
----- Method: RichRunner>>initScheduler (in category 'private') -----
initScheduler
queuePacketCount := holdCount := 0.
taskTable := Array new: 6.
taskList := nil!
----- Method: RichRunner>>queuePacket: (in category 'private') -----
queuePacket: packet
| t |
t := self findTask: packet identity.
t ifNil: [ ^ nil ].
queuePacketCount := queuePacketCount + 1.
packet link: nil.
packet identity: currentTaskIdentity.
^ t addInput: packet checkPriority: currentTask!
----- Method: RichRunner>>release: (in category 'private') -----
release: identity
| t |
t := self findTask: identity.
t ifNil: [ ^ nil ].
t taskHolding: false.
^ t priority > currentTask priority
ifTrue: [ t ]
ifFalse: [ currentTask ]!
----- Method: RichRunner>>schedule (in category 'scheduling') -----
schedule
currentTask := taskList.
[currentTask isNil]
whileFalse:
[currentTask isTaskHoldingOrWaiting
ifTrue: [currentTask := currentTask link]
ifFalse:
[currentTaskIdentity := currentTask identity.
currentTask := currentTask runTask]]!
----- Method: RichRunner>>start (in category 'initialize') -----
start
| workQ |
self initScheduler.
self
createIdler: Idler
priority: 0
work: nil
state: RichTaskState running.
workQ := self
createPacket: nil
identity: Worker
kind: WorkPacketKind.
workQ := self
createPacket: workQ
identity: Worker
kind: WorkPacketKind.
self
createWorker: Worker
priority: 1000
work: workQ
state: RichTaskState waitingWithPacket.
workQ := self
createPacket: nil
identity: DeviceA
kind: DevicePacketKind.
workQ := self
createPacket: workQ
identity: DeviceA
kind: DevicePacketKind.
workQ := self
createPacket: workQ
identity: DeviceA
kind: DevicePacketKind.
self
createHandler: HandlerA
priority: 2000
work: workQ
state: RichTaskState waitingWithPacket.
workQ := self
createPacket: nil
identity: DeviceB
kind: DevicePacketKind.
workQ := self
createPacket: workQ
identity: DeviceB
kind: DevicePacketKind.
workQ := self
createPacket: workQ
identity: DeviceB
kind: DevicePacketKind.
self
createHandler: HandlerB
priority: 3000
work: workQ
state: RichTaskState waitingWithPacket.
self
createDevice: DeviceA
priority: 4000
work: nil
state: RichTaskState waiting.
self
createDevice: DeviceB
priority: 5000
work: nil
state: RichTaskState waiting.
self schedule.
queuePacketCount = 23246 & (holdCount = 9297) ifFalse: [self error: 'wrong result'].
!
----- Method: RichRunner>>wait (in category 'private') -----
wait
currentTask taskWaiting: true.
^ currentTask!
RichObject subclass: #RichTaskState
instanceVariableNames: 'packetPendingIV taskHolding taskWaiting'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichTaskState commentStamp: '<historical>' prior: 0!
Abstract task that manipulates work packets.!
RichTaskState subclass: #RichTaskControlBlock
instanceVariableNames: 'link identity priority input state function handle'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichTaskControlBlock commentStamp: '<historical>' prior: 0!
A task control block manages a task and the queue of work packages associated with it.!
----- Method: RichTaskControlBlock class>>link:create:priority:initialWorkQueue:initialState:function:privateData: (in category 'instance creation') -----
link: link create: identity priority: priority initialWorkQueue: initialWorkQueue initialState: initialState function: aBlock privateData: privateData
^ self new
link: link
identity: identity
priority: priority
initialWorkQueue: initialWorkQueue
initialState: initialState
function: aBlock
privateData: privateData!
----- Method: RichTaskControlBlock>>addInput:checkPriority: (in category 'scheduling') -----
addInput: packet checkPriority: oldTask
input
ifNil:
[input := packet.
packetPendingIV := true.
priority > oldTask priority ifTrue: [ ^ self ]]
ifNotNil:
[ input := self append: packet head: input ].
^ oldTask!
----- Method: RichTaskControlBlock>>identity (in category 'accessing') -----
identity
^ identity!
----- Method: RichTaskControlBlock>>link (in category 'accessing') -----
link
^ link!
----- Method: RichTaskControlBlock>>link:identity:priority:initialWorkQueue:initialState:function:privateData: (in category 'initialize') -----
link: aLink identity: anIdentity priority: aPriority initialWorkQueue: anInitialWorkQueue initialState: anInitialState function: aBlock privateData: aPrivateData
link := aLink.
identity := anIdentity.
priority := aPriority.
input := anInitialWorkQueue.
packetPendingIV := anInitialState isPacketPending.
taskWaiting := anInitialState isTaskWaiting.
taskHolding := anInitialState isTaskHolding.
function := aBlock.
handle := aPrivateData!
----- Method: RichTaskControlBlock>>priority (in category 'accessing') -----
priority
^ priority!
----- Method: RichTaskControlBlock>>runTask (in category 'scheduling') -----
runTask
| message |
self isWaitingWithPacket
ifTrue:
[message := input.
input := message link.
input
ifNil: [self running]
ifNotNil: [self packetPending]]
ifFalse: [message := nil].
^ function value: message value: handle!
----- Method: RichTaskState class>>packetPending (in category 'instance creation') -----
packetPending
^super new packetPending!
----- Method: RichTaskState class>>running (in category 'instance creation') -----
running
^super new running!
----- Method: RichTaskState class>>waiting (in category 'instance creation') -----
waiting
^super new waiting!
----- Method: RichTaskState class>>waitingWithPacket (in category 'instance creation') -----
waitingWithPacket
^super new waitingWithPacket!
----- Method: RichTaskState>>isPacketPending (in category 'testing') -----
isPacketPending
^packetPendingIV!
----- Method: RichTaskState>>isRunning (in category 'testing') -----
isRunning
^packetPendingIV not and: [taskWaiting not and: [taskHolding not]]!
----- Method: RichTaskState>>isTaskHolding (in category 'testing') -----
isTaskHolding
^taskHolding!
----- Method: RichTaskState>>isTaskHoldingOrWaiting (in category 'testing') -----
isTaskHoldingOrWaiting
^taskHolding or: [packetPendingIV not and: [taskWaiting]]!
----- Method: RichTaskState>>isTaskWaiting (in category 'testing') -----
isTaskWaiting
^taskWaiting!
----- Method: RichTaskState>>isWaiting (in category 'testing') -----
isWaiting
^packetPendingIV not and: [taskWaiting and: [taskHolding not]]!
----- Method: RichTaskState>>isWaitingWithPacket (in category 'testing') -----
isWaitingWithPacket
^packetPendingIV and: [taskWaiting and: [taskHolding not]]!
----- Method: RichTaskState>>packetPending (in category 'initialize') -----
packetPending
packetPendingIV := true.
taskWaiting := false.
taskHolding := false!
----- Method: RichTaskState>>running (in category 'initialize') -----
running
packetPendingIV := taskWaiting := taskHolding := false!
----- Method: RichTaskState>>taskHolding: (in category 'accessing') -----
taskHolding: anObject
^ taskHolding := anObject!
----- Method: RichTaskState>>taskWaiting: (in category 'accessing') -----
taskWaiting: anObject
^ taskWaiting := anObject!
----- Method: RichTaskState>>waiting (in category 'initialize') -----
waiting
packetPendingIV := taskHolding := false.
taskWaiting := true!
----- Method: RichTaskState>>waitingWithPacket (in category 'initialize') -----
waitingWithPacket
taskHolding := false.
taskWaiting := packetPendingIV := true!
RichObject subclass: #RichWorkerTaskDataRecord
instanceVariableNames: 'destination count'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!RichWorkerTaskDataRecord commentStamp: '<historical>' prior: 0!
A task that manipulates work packets.!
----- Method: RichWorkerTaskDataRecord>>count (in category 'accessing') -----
count
^ count!
----- Method: RichWorkerTaskDataRecord>>count: (in category 'accessing') -----
count: anObject
count := anObject!
----- Method: RichWorkerTaskDataRecord>>destination (in category 'accessing') -----
destination
^ destination!
----- Method: RichWorkerTaskDataRecord>>destination: (in category 'accessing') -----
destination: anObject
destination := anObject!
----- Method: RichWorkerTaskDataRecord>>initialize (in category 'as yet unclassified') -----
initialize
destination := HandlerA.
count := 0 !
Object subclass: #SMarkHarness
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkHarness commentStamp: '<historical>' prior: 0!
A benchmark harness steers the execution and reporting of benchmarks.
For that purpose, it will use a designated benchmark runner to do the execution and a benchmark reporter to output the results.
The benchmark harness is also parameterized by the benchmark suites that are to be executed.
The simplest way to execute a benchmark suite is to use SMarkSuite >> #run.
However, directly using the harness classes gives more freedom on reporting and execution strategies.
A typical call of the harness from the commandline would result in the following invokation:
SMarkHarness run: {'SMarkHarness'. 'SMarkLoops.benchIntLoop'. 1. 1. 5}!
SMarkHarness subclass: #ReBenchHarness
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!ReBenchHarness commentStamp: 'StefanMarr 5/16/2011 09:10' prior: 0!
The ReBenchHarness is optimized for use from the command-line.
It is especially meant to be used together with ReBench, a tool to execute and document benchmarks reproducibly.
See: https://github.com/smarr/ReBench#readme!
----- Method: ReBenchHarness class>>defaultArgumentParser (in category 'helper') -----
defaultArgumentParser
^ ReBenchHarnessArgumentParser!
----- Method: ReBenchHarness class>>defaultReporter (in category 'defaults') -----
defaultReporter
^ ReBenchReporter!
----- Method: ReBenchHarness class>>defaultRunner (in category 'defaults') -----
defaultRunner
^ SMarkWeakScalingRunner!
----- Method: ReBenchHarness class>>usageBenchmarkParameters: (in category 'helper') -----
usageBenchmarkParameters: usage
^ usage, ' processes optional, number of processes/threads used by the benchmarks', String crlf,
' inner-iterations optional, number of iterations done by a single process', String crlf,
' problemSize optional, depending on benchmark for instance size of used data set', String crlf.
!
----- Method: ReBenchHarness class>>usageHeader (in category 'helper') -----
usageHeader
| usage |
usage := 'SMark Benchmark Framework, version: ', self version, String crlf.
usage := usage, String crlf.
usage := usage, 'Usage: <vm+image> ', self name,
' <suiteOrBenchmark> [processes [inner-iterations [problemSize]]]', String crlf.
usage := usage, String crlf.
usage := usage, ' This harness is used for weak-scalling benchmarks.', String crlf.
usage := usage, ' Use the SMarkHarness for more general settings, it offers more options.', String crlf.
usage := usage, String crlf.
^ usage!
----- Method: ReBenchHarness class>>usageReporter: (in category 'helper') -----
usageReporter: usage
"Will rely on default, which is good for ReBench, so do not advertise option."
^ usage!
----- Method: ReBenchHarness class>>usageRunner: (in category 'helper') -----
usageRunner: usage
"Will rely on default, which is good for ReBench, so do not advertise option."
^ usage!
----- Method: SMarkHarness class>>defaultArgumentParser (in category 'helper') -----
defaultArgumentParser
^ SMarkHarnessArgumentParser!
----- Method: SMarkHarness class>>defaultOutputDestination (in category 'defaults') -----
defaultOutputDestination
^ Smalltalk at: #ScriptConsole
ifAbsent: [SMarkReporter defaultOutputDestination]!
----- Method: SMarkHarness class>>defaultReporter (in category 'defaults') -----
defaultReporter
^ SMarkReporter defaultReporter!
----- Method: SMarkHarness class>>defaultRunner (in category 'defaults') -----
defaultRunner
^ SMarkSuite defaultRunner!
----- Method: SMarkHarness class>>execute:andReport: (in category 'benchmarking') -----
execute: runner andReport: reporter
runner reportConfiguration: self defaultOutputDestination.
runner execute.
reporter runner: runner.
reporter outputStream: self defaultOutputDestination.
reporter report.!
----- Method: SMarkHarness class>>execute:using:andReport: (in category 'benchmarking') -----
execute: aBenchmarkOrSuite using: aRunnerClass andReport: withAReporterClass
| parsedBenchmarkOrSuite runner reporter |
parsedBenchmarkOrSuite := self parseBenchmarkOrSuite: aBenchmarkOrSuite.
runner := aRunnerClass new.
reporter := withAReporterClass new.
self instructRunner: runner with: parsedBenchmarkOrSuite.
self execute: runner andReport: reporter. !
----- Method: SMarkHarness class>>parseArguments: (in category 'helper') -----
parseArguments: arguments
| parser |
parser := self defaultArgumentParser new.
parser harness: self.
^ parser parse: arguments.!
----- Method: SMarkHarness class>>run: (in category 'script entry') -----
run: arguments
"Executed from the command line using something similar to
./vm my.image SMarkHarness SMarkRunner SMarkReporter SMarkLoops\>\>benchIntLoop 1 1 5
./vm my.image SMarkHarness SMarkRunner SMarkReporter SMarkLoops.benchIntLoop 1 1 5"
| runner reporter runnerAndReporter |
(self shouldShowUsage: arguments)
ifTrue: [
self usage.
^ self.
].
runnerAndReporter := self parseArguments: arguments.
runner := runnerAndReporter first.
reporter := runnerAndReporter second.
self execute: runner andReport: reporter. !
----- Method: SMarkHarness class>>shouldShowUsage: (in category 'helper') -----
shouldShowUsage: arguments
arguments size < 2 ifTrue: [^ true ].
^ arguments anySatisfy: [:elem | (elem = '--help') or: [elem = '-?'] ]. !
----- Method: SMarkHarness class>>usage (in category 'helper') -----
usage
| usage |
"Example usage: SMarkHarness SMarkRunner SMarkReporter SMarkLoops.benchIntLoop 1 1 5"
usage := self usageHeader.
usage := usage, 'Arguments:', String crlf.
usage := self usageRunner: usage.
usage := self usageReporter: usage.
usage := usage, ' suiteOrBenchmark required, either a SMarkSuite with benchmarks,', String crlf.
usage := usage, ' or a benchmark denoted by Suite.benchName', String crlf.
usage := self usageBenchmarkParameters: usage.
self defaultOutputDestination print: usage.!
----- Method: SMarkHarness class>>usageBenchmarkParameters: (in category 'helper') -----
usageBenchmarkParameters: usage
^ usage, ' iterations optional, number of times the benchmarks are repeated', String crlf,
' processes optional, number of processes/threads used by the benchmarks', String crlf,
' problemSize optional, depending on benchmark for instance number of', String crlf,
' inner iterations or size of used data set', String crlf.
!
----- Method: SMarkHarness class>>usageHeader (in category 'helper') -----
usageHeader
| usage |
usage := 'SMark Benchmark Framework, version: ', self version, String crlf.
usage := usage, String crlf.
usage := usage, 'Usage: <vm+image> ', self name, ' [runner] [reporter] <suiteOrBenchmark>', String crlf.
usage := usage, ' [iterations [processes [problemSize]]]', String crlf.
usage := usage, String crlf.
^ usage!
----- Method: SMarkHarness class>>usageReporter: (in category 'helper') -----
usageReporter: usage
^ usage, ' reporter optional, a SMarkReporter class that processes', String crlf,
' and displays the results', String crlf.
!
----- Method: SMarkHarness class>>usageRunner: (in category 'helper') -----
usageRunner: usage
^ usage, ' runner optional, a SMarkRunner class that executes the benchmarks', String crlf.!
----- Method: SMarkHarness class>>version (in category 'helper') -----
version
(Smalltalk classNamed: #ConfigurationOfBenchmarking)
ifNotNilDo: [:cfg |
^ cfg project currentVersion versionNumber asString.
].
(Smalltalk classNamed: #MCPackage)
ifNotNilDo: [:mcp |
| package |
package := mcp named: 'SMark'.
package hasWorkingCopy ifTrue: [
^ package workingCopy ancestors first name.
].
].
^ ''.!
Object subclass: #SMarkHarnessArgumentParser
instanceVariableNames: 'runner reporter suiteOrBenchmark iterations processes problemSize i current numParams currentObj arguments suite specificBenchmark suiteClass harness'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
SMarkHarnessArgumentParser subclass: #ReBenchHarnessArgumentParser
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
----- Method: ReBenchHarnessArgumentParser>>determineBenchmarkParametersFromArguments (in category 'argument parsing') -----
determineBenchmarkParametersFromArguments
i := i + 1.
i <= numParams ifTrue: [
processes := (arguments at: i) asInteger.
i := i + 1.
i <= numParams ifTrue: [
iterations := (arguments at: i) asInteger.
i := i + 1.
i <= numParams ifTrue: [
problemSize := arguments at: i.
]
]
].!
----- Method: ReBenchHarnessArgumentParser>>determineReporter (in category 'argument parsing') -----
determineReporter
reporter := harness defaultReporter new.!
----- Method: ReBenchHarnessArgumentParser>>determineRunner (in category 'argument parsing') -----
determineRunner
runner := harness defaultRunner new.!
----- Method: ReBenchHarnessArgumentParser>>instructRunner (in category 'helper') -----
instructRunner
super instructRunner.
runner iterations: runner class defaultNumberOfIterations.
runner innerIterations: iterations.!
----- Method: SMarkHarnessArgumentParser>>determineBenchmarkParameters (in category 'argument parsing') -----
determineBenchmarkParameters
"Initialize with defaults, will be overwritten in case
it is specified."
iterations := runner class defaultNumberOfIterations.
processes := runner class defaultNumberOfProcesses.
problemSize:= suiteClass defaultProblemSize.
self determineBenchmarkParametersFromArguments.!
----- Method: SMarkHarnessArgumentParser>>determineBenchmarkParametersFromArguments (in category 'argument parsing') -----
determineBenchmarkParametersFromArguments
i := i + 1.
i <= numParams ifTrue: [
iterations := (arguments at: i) asInteger.
i := i + 1.
i <= numParams ifTrue: [
processes := (arguments at: i) asInteger.
i := i + 1.
i <= numParams ifTrue: [
problemSize := arguments at: i.
]
]
].!
----- Method: SMarkHarnessArgumentParser>>determineReporter (in category 'argument parsing') -----
determineReporter
(currentObj isKindOf: SMarkReporter)
ifFalse: [ reporter := harness defaultReporter new. ]
ifTrue: [ reporter := currentObj.
i := i + 1.
i <= numParams ifTrue: [
current := arguments at: i.
]
].!
----- Method: SMarkHarnessArgumentParser>>determineRunner (in category 'argument parsing') -----
determineRunner
(currentObj isKindOf: SMarkRunner)
ifFalse: [ runner := harness defaultRunner new. ]
ifTrue: [ runner := currentObj.
i := i + 1.
i <= numParams ifTrue: [
current := arguments at: i.
currentObj := (Smalltalk classNamed: current) ifNotNilDo: [:cls | cls new].
]
].!
----- Method: SMarkHarnessArgumentParser>>determineSuiteOrBenchmark (in category 'argument parsing') -----
determineSuiteOrBenchmark
self parseBenchmarkOrSuite: current.!
----- Method: SMarkHarnessArgumentParser>>harness: (in category 'accessing') -----
harness: aHarness
harness := aHarness!
----- Method: SMarkHarnessArgumentParser>>instructRunner (in category 'helper') -----
instructRunner
suite := suiteClass new.
specificBenchmark ifNotNil: [
suite runOnly: specificBenchmark.
].
runner suite: suite.
runner iterations: iterations.
runner processes: processes.
runner problemSize: problemSize.!
----- Method: SMarkHarnessArgumentParser>>parse: (in category 'parsing') -----
parse: argumentsArray
arguments := argumentsArray.
numParams := arguments size.
i := 2.
current := arguments at: i.
currentObj := (Smalltalk classNamed: current) ifNotNilDo: [:cls | cls new].
self determineRunner.
self determineReporter.
self determineSuiteOrBenchmark.
self determineBenchmarkParameters.
self instructRunner.
^ {runner. reporter}!
----- Method: SMarkHarnessArgumentParser>>parseBenchmarkOrSuite: (in category 'argument parsing') -----
parseBenchmarkOrSuite: aBenchmarkOrSuite
"Identify the benchmark suite or suite and benchmark method
that should be executed. The string should be of the format 'Class>>benchName' or 'Class.benchName' for shell/bash compatibility.
Accepts a string, class, or array.
Returns, a class, or an array of a class and a symbol."
| parsed |
(aBenchmarkOrSuite isKindOf: Class)
ifTrue: [
suiteClass := aBenchmarkOrSuite.
^ suiteClass
].
(aBenchmarkOrSuite isKindOf: Array)
ifTrue: [ parsed := aBenchmarkOrSuite. ]
ifFalse: [ parsed := aBenchmarkOrSuite findTokens: '>.'. ].
((parsed size > 2) or: [parsed size < 1])
ifTrue: [ Error signal: 'The passed argument has to represent two elements. A class/classname and a method symbol' ].
suiteClass := parsed first.
(suiteClass isKindOf: Class)
ifFalse: [ suiteClass := Smalltalk at: (suiteClass asSymbol) ifAbsent: [Error signal: 'Class that was supposed to represent a benchmark suite was not found: ', suiteClass asString ]].
parsed size = 1
ifTrue: [^suiteClass].
specificBenchmark := parsed second asSymbol.
^ { suiteClass. specificBenchmark }
!
Object subclass: #SMarkReporter
instanceVariableNames: 'runner stream'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkReporter commentStamp: '<historical>' prior: 0!
SMarkReporter is a simple formatter of benchmark results.
Subclass such as SMarkSimpleStatisticsReporter might implement more advanced reporting functionality, e.g., including a statistical evaluation of the results.
Example:
| f |
f := TextStream on: String new.
SMarkSimpleStatisticsReporter reportFor: (SMarkTestRunnerSuiteForAutosizing run: 10) on: f.
f contents!
----- Method: SMarkReporter class>>defaultOutputDestination (in category 'defaults') -----
defaultOutputDestination
^ ScriptConsole!
----- Method: SMarkReporter class>>defaultReporter (in category 'defaults') -----
defaultReporter
^ SMarkSimpleStatisticsReporter!
----- Method: SMarkReporter class>>reportFor: (in category 'reporting') -----
reportFor: aRunner
self reportFor: aRunner on: self defaultOutputDestination.!
----- Method: SMarkReporter class>>reportFor:on: (in category 'reporting') -----
reportFor: aRunner on: aStream
| reporter |
reporter := self new.
reporter runner: aRunner.
reporter outputStream: aStream.
reporter report.
^ reporter.!
----- Method: SMarkReporter>>benchmarkFooter: (in category 'reporting') -----
benchmarkFooter: aName
stream cr.!
----- Method: SMarkReporter>>benchmarkHeader: (in category 'reporting') -----
benchmarkHeader: aName
stream << 'Benchmark ' << (aName asString); cr.!
----- Method: SMarkReporter>>footer (in category 'reporting') -----
footer
"No output at the moment"
^ self!
----- Method: SMarkReporter>>header (in category 'reporting') -----
header
| suiteName |
suiteName := runner suite class name asString.
stream << 'Report for: ' << suiteName; cr.!
----- Method: SMarkReporter>>initialize (in category 'initialization') -----
initialize
super initialize.
stream := self class defaultOutputDestination.!
----- Method: SMarkReporter>>outputStream: (in category 'accessing') -----
outputStream: aStream
stream := aStream !
----- Method: SMarkReporter>>report (in category 'accessing') -----
report
self header.
runner results keysAndValuesDo: [:key :value |
self benchmarkHeader: key.
self reportAllRuns: value of: key.
self benchmarkFooter: key.
].
self footer.
^ self!
----- Method: SMarkReporter>>reportAllRuns:of: (in category 'reporting') -----
reportAllRuns: aListOfResults of: benchmark
aListOfResults do: [:result |
result criteria keysAndValuesDo: [:benchName :timer |
stream << benchName << ': ' << (timer totalTime asString, 'ms'); cr.]]!
----- Method: SMarkReporter>>runner: (in category 'accessing') -----
runner: aRunner
runner := aRunner.!
SMarkReporter subclass: #SMarkSimpleStatisticsReporter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
SMarkSimpleStatisticsReporter subclass: #ReBenchReporter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!ReBenchReporter commentStamp: '<historical>' prior: 0!
A ReBenchReporter is reporter for the ReBench framework (cf. https://github.com/smarr/ReBench).
It is used be the ReBenchHarness, which is the main class of interest for users that just want to execute benchmarks.!
----- Method: ReBenchReporter>>benchmarkHeader: (in category 'as yet unclassified') -----
benchmarkHeader: aName
^ self!
----- Method: SMarkSimpleStatisticsReporter>>confidenceVariance: (in category 'statistics') -----
confidenceVariance: times
| numMeasurements |
numMeasurements := times size.
(numMeasurements >= 30)
ifTrue: [
^ (self gaussianConfidenceFactor) * (times stdev) / (numMeasurements asFloat sqrt)].
"use the students T distribution for small probe counts"
^ (self studentsTConfidenceFactorFor: numMeasurements) * (times stdev) / (numMeasurements asFloat sqrt)!
----- Method: SMarkSimpleStatisticsReporter>>gaussianConfidenceFactor (in category 'statistics') -----
gaussianConfidenceFactor
"used for large probe counts >= 30"
"1 ~ 68.27%"
"1.644853626951 ~ 90%"
"2 ~ 95.45%"
^ 1.644853626951!
----- Method: SMarkSimpleStatisticsReporter>>reportAllRuns:of: (in category 'reporting') -----
reportAllRuns: aListOfResults of: benchmark
| criteria |
criteria := aListOfResults first criteria.
criteria keysDo: [:criterion |
| times |
times := self resultsFor: criterion from: aListOfResults.
self reportResult: times for: criterion of: benchmark.
stream cr.
].!
----- Method: SMarkSimpleStatisticsReporter>>reportResult:for:of: (in category 'reporting') -----
reportResult: aResultsArray for: aCriterion of: benchmark
| convidenceVariance significantDigits |
stream << benchmark << ' ' << aCriterion << ': iterations='.
aResultsArray size printOn: stream .
stream << ' runtime: '.
aResultsArray size < 2 ifTrue: [
aResultsArray average printOn: stream.
stream << 'ms'.
^ self.
].
convidenceVariance := self confidenceVariance: aResultsArray.
"only print significant "
significantDigits := self significantDigits: convidenceVariance.
aResultsArray average printOn: stream showingDecimalPlaces: significantDigits.
stream << 'ms +/-'.
convidenceVariance printOn: stream showingDecimalPlaces: significantDigits.!
----- Method: SMarkSimpleStatisticsReporter>>resultsFor:from: (in category 'helper') -----
resultsFor: aCriterion from: aListOfResults
^aListOfResults collect: [:result | (result criteria at: aCriterion) totalTime]
!
----- Method: SMarkSimpleStatisticsReporter>>significantDigits: (in category 'statistics') -----
significantDigits: confidenceVariance
confidenceVariance = 0
ifTrue: [ ^ 2].
confidenceVariance >= 10
ifTrue: [ ^ 0].
^ 1 - (confidenceVariance log floor)!
----- Method: SMarkSimpleStatisticsReporter>>studentsTConfidenceFactorFor: (in category 'statistics') -----
studentsTConfidenceFactorFor: aNumberOfMeasurements
"used for small probe counts < 30"
"the students T distribution sucks to calculate since the value depends on the probeCout"
"these values are for a confidence interval of ~90%"
| values |
values := Array new: 30.
values at: 1 put: 6.314.
values at: 2 put: 2.920.
values at: 3 put: 2.353.
values at: 4 put: 2.132.
values at: 5 put: 2.015.
values at: 6 put: 1.943.
values at: 7 put: 1.895.
values at: 8 put: 1.860.
values at: 9 put: 1.833.
values at: 10 put: 1.812.
values at: 11 put: 1.796.
values at: 12 put: 1.782.
values at: 13 put: 1.771.
values at: 14 put: 1.761.
values at: 15 put: 1.753.
values at: 16 put: 1.746.
values at: 17 put: 1.740.
values at: 18 put: 1.734.
values at: 19 put: 1.729.
values at: 20 put: 1.725.
values at: 21 put: 1.721.
values at: 22 put: 1.717.
values at: 23 put: 1.714.
values at: 24 put: 1.711.
values at: 25 put: 1.708.
values at: 26 put: 1.706.
values at: 27 put: 1.703.
values at: 28 put: 1.701.
values at: 29 put: 1.699.
values at: 30 put: 1.697.
^ values at: aNumberOfMeasurements.
!
----- Method: SMarkSimpleStatisticsReporter>>totalResultsFor: (in category 'helper') -----
totalResultsFor: aListOfResults
^aListOfResults collect: [:timer | timer total]
!
Object subclass: #SMarkResult
instanceVariableNames: 'time benchName suite criteria'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkResult commentStamp: 'StefanMarr 3/18/2011 23:45' prior: 0!
A benchmark result is characterized by:
- the total execution time (#total is the least a benchmark results in)
- the name of the benchmark that was executed
- the suite object specifies the used input used for the benchmark
- dictionary of additional the criteria and the related timings
A benchmark can produced multiple resuts for different criteria. The standard criterion is #total.!
----- Method: SMarkResult>>benchmarkName (in category 'accessing') -----
benchmarkName
^ benchName!
----- Method: SMarkResult>>benchmarkName: (in category 'accessing') -----
benchmarkName: aString
benchName := aString!
----- Method: SMarkResult>>criteria (in category 'accessing') -----
criteria
^ criteria!
----- Method: SMarkResult>>criteria: (in category 'accessing') -----
criteria: aCollectionOfTimers
criteria := aCollectionOfTimers!
----- Method: SMarkResult>>suite (in category 'accessing') -----
suite
^ suite!
----- Method: SMarkResult>>suite: (in category 'accessing') -----
suite: aBenchmarkSuite
suite := aBenchmarkSuite!
----- Method: SMarkResult>>total (in category 'accessing') -----
total
^ time!
----- Method: SMarkResult>>total: (in category 'accessing') -----
total: aTime
time := aTime!
Object subclass: #SMarkRunner
instanceVariableNames: 'numIterations suite runner results currentBenchmark timers problemSize numProcesses'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
SMarkRunner subclass: #SMarkAutosizeRunner
instanceVariableNames: 'targetTime innerLoopIterations'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
----- Method: SMarkAutosizeRunner class>>defaultTargetTime (in category 'defaults') -----
defaultTargetTime
"300 milliseconds seems to be a reasonable target time for most problems.
It is a compromise between the general measurment noise as well as timer accuracy
and the absolute runtime of benchmarks"
^ 300!
----- Method: SMarkAutosizeRunner>>initialize (in category 'initialization') -----
initialize
super initialize.
targetTime := self class defaultTargetTime.!
----- Method: SMarkAutosizeRunner>>performBenchmark: (in category 'benchmarking') -----
performBenchmark: aSelector
"First determine a useful number of inner loop iterations until the targetTime is reached."
| execTime i |
"make sure no timers are recorded for this"
timers := nil.
i := 1.
execTime := Time millisecondsToRun: [ suite perform: aSelector. ].
[ execTime > targetTime ] whileFalse: [
i := i * 2. "Was thinking of doing something fancy here, but just go with this simple staight-forward solution"
execTime := Time millisecondsToRun: [ 1 to: i do: [:ignored| suite perform: aSelector]].
].
innerLoopIterations := i.
"Then start executing the benchmark"
^ super performBenchmark: aSelector.!
----- Method: SMarkAutosizeRunner>>runBaseBenchmark (in category 'benchmarking') -----
runBaseBenchmark
"baseBenchmark is not supported with autosizing. I do not see how that can be made possible since all different benchmarks will have different number of iterations, and the only way how a consistent baseline could be found would be to normalize the results, but well, incooprorating the baseline measurement with the statistical evaluation is harder than just substracting a one time value..., I am not going to do that here for the moment. Stefan 2011-03-20"
(suite respondsTo: #baseBenchmark)
ifFalse: [ ^ nil ].
"I decided to go here with a silent solution to avoid thinking about logging frameworks and Transcript to console convertion..."
self recordResults: (self class defaultTimer new: 'total') for: #baseBenchmark !
----- Method: SMarkAutosizeRunner>>targetTime (in category 'accessing') -----
targetTime
"Target time in milliseconds"
^ targetTime!
----- Method: SMarkAutosizeRunner>>targetTime: (in category 'accessing') -----
targetTime: anIntInMilliseconds
"Target time in milliseconds"
targetTime := anIntInMilliseconds!
----- Method: SMarkAutosizeRunner>>timedBenchmarkExecution: (in category 'benchmarking') -----
timedBenchmarkExecution: aSelector
"Will do a timed execution of the benchmark and return the result timer"
| timer |
timers := Dictionary new.
timer := self createTimer: 'total'.
timer start.
1 to: innerLoopIterations do: [:ignored|
suite perform: aSelector.
].
timer stop.
self recordResults: timer for: aSelector.
^ timer!
SMarkRunner subclass: #SMarkCogRunner
instanceVariableNames: 'warmingUp'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkCogRunner commentStamp: 'StefanMarr 12/30/2011 23:24' prior: 0!
This runner is doing warmup on for Cog VMs with just-in-time compilation.
The goal is to bring the JIT compiler into a steady state where no jitting is performed anymore during benchmarking.!
----- Method: SMarkCogRunner>>initialize (in category 'initialization') -----
initialize
super initialize.
warmingUp := false.!
----- Method: SMarkCogRunner>>performBenchmark: (in category 'initialization') -----
performBenchmark: aSelector
"Based on an email by Eliot from May 16th, 2011.
The first time a method is executed it will get into the inline cache.
The second time, it is found in the inline cache, which triggers the JIT compiler to produce code.
Thus, the third time it should be executed in the steady state."
warmingUp := true.
suite runBenchmark: aSelector.
Smalltalk garbageCollect.
suite runBenchmark: aSelector.
Smalltalk garbageCollect.
warmingUp := false.
^ super performBenchmark: aSelector.!
----- Method: SMarkCogRunner>>recordResults:for: (in category 'initialization') -----
recordResults: timer for: aSelector
"Only record the results when we are not in warmup mode."
warmingUp ifFalse: [
super recordResults: timer for: aSelector.
].!
SMarkRunner subclass: #SMarkProfileRunner
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkProfileRunner commentStamp: '<historical>' prior: 0!
This runner profiles the benchmarks for better analysis. Unlike the classical benchmark runner this one will not collect the results. Instead it will execute the benchmarks in the system profiler.!
----- Method: SMarkProfileRunner class>>execute:selector: (in category 'profiling') -----
execute: aSuite selector: aBenchmarkSelector
| runner |
runner := self new.
aSuite runner: runner.
runner
suite: aSuite;
execute: aBenchmarkSelector.
^ runner!
----- Method: SMarkProfileRunner class>>execute:selector:iterations: (in category 'profiling') -----
execute: aSuite selector: aBenchmarkSelector iterations: nIterations
| runner |
runner := self new.
aSuite runner: runner.
runner
suite: aSuite;
iterations: nIterations;
execute: aBenchmarkSelector.
^ runner!
----- Method: SMarkProfileRunner>>execute (in category 'profiling') -----
execute
"run all benchmnarks in a benchmark suite "
[ suite run ] timeProfile!
----- Method: SMarkProfileRunner>>execute: (in category 'profiling') -----
execute: aSelector
[ self performBenchmark: aSelector ] timeProfile!
----- Method: SMarkProfileRunner>>initialize (in category 'initialization') -----
initialize
super initialize.
numIterations := 1.!
----- Method: SMarkProfileRunner>>performBenchmark: (in category 'benchmarks') -----
performBenchmark: aSelector
currentBenchmark := aSelector.
1 to: numIterations do: [:i|
suite runBenchmark: aSelector ].
currentBenchmark := nil.!
----- Method: SMarkProfileRunner>>timedBenchmarkExecution: (in category 'benchmarks') -----
timedBenchmarkExecution: aSelector
suite perform: aSelector!
----- Method: SMarkRunner class>>defaultNumberOfIterations (in category 'defaults') -----
defaultNumberOfIterations
^ 1!
----- Method: SMarkRunner class>>defaultNumberOfProcesses (in category 'defaults') -----
defaultNumberOfProcesses
^ 1!
----- Method: SMarkRunner class>>defaultTimer (in category 'defaults') -----
defaultTimer
^ SMarkTimer!
----- Method: SMarkRunner class>>execute: (in category 'benchmarking') -----
execute: aSuite
^ self execute: aSuite with: 1.!
----- Method: SMarkRunner class>>execute:with: (in category 'benchmarking') -----
execute: aSuite with: nIterations
| runner |
runner := self new.
aSuite runner: runner.
runner suite: aSuite.
runner iterations: nIterations.
runner execute.
^ runner!
----- Method: SMarkRunner>>createTimer: (in category 'helper') -----
createTimer: name
"Create and register a new timer for the current benchmark"
| timer |
timer := self class defaultTimer new: name.
timers ifNotNil: [
timers at: name put: timer.
].
^ timer.!
----- Method: SMarkRunner>>execute (in category 'execution') -----
execute
suite run.
self runBaseBenchmark.
^ results
!
----- Method: SMarkRunner>>initialize (in category 'initialization') -----
initialize
super initialize.
numIterations := self class defaultNumberOfIterations.
numProcesses := self class defaultNumberOfProcesses.
results := Dictionary new.!
----- Method: SMarkRunner>>iterations (in category 'accessing') -----
iterations
^ numIterations!
----- Method: SMarkRunner>>iterations: (in category 'accessing') -----
iterations: anInteger
numIterations := anInteger!
----- Method: SMarkRunner>>performBenchmark: (in category 'benchmarking') -----
performBenchmark: aSelector
currentBenchmark := aSelector.
1 to: numIterations do: [:i|
"self timedBenchmarkExecution: aSelector."
suite runBenchmark: aSelector.
].
currentBenchmark := nil.
^ results at: (suite benchmarkNameForSelector: aSelector)!
----- Method: SMarkRunner>>printOn: (in category 'printing') -----
printOn: aStream
^ self reportOn: aStream.!
----- Method: SMarkRunner>>problemSize (in category 'accessing') -----
problemSize
<omniUnenforced> "Hint for the OMOP that it is part of the meta infrastructure"
^ problemSize!
----- Method: SMarkRunner>>problemSize: (in category 'accessing') -----
problemSize: aValue
"Do some conversion to make it easier for the benchmarks"
(aValue isString and: [aValue isAllDigits]) ifTrue: [
problemSize := Number readFrom: aValue.
^ self.
].
problemSize := aValue!
----- Method: SMarkRunner>>processes (in category 'accessing') -----
processes
"The standard runner does use only a single process, but in case a benchmark supports parallelism it can query for the intended degree of parallelism"
^ numProcesses!
----- Method: SMarkRunner>>processes: (in category 'accessing') -----
processes: anInt
"The standard runner does use only a single process, but a benchmark can use that to do its own parallelism"
numProcesses := anInt!
----- Method: SMarkRunner>>recordResults:for: (in category 'helper') -----
recordResults: timer for: aSelector
| result name |
name := suite benchmarkNameForSelector: aSelector.
result := SMarkResult new.
result total: timer totalTime.
result benchmarkName: name.
result suite: suite.
result criteria: timers.
(results at: name ifAbsentPut: OrderedCollection new) add: result.!
----- Method: SMarkRunner>>report (in category 'accessing') -----
report
SMarkReporter defaultReporter reportFor: self.
!
----- Method: SMarkRunner>>reportConfiguration: (in category 'reporting') -----
reportConfiguration: aStream
aStream << 'Runner Configuration:';cr.
aStream << (' iterations: ', numIterations asString); cr.
aStream << (' processes: ', numProcesses asString); cr.
aStream << (' problem size: ', problemSize asString); cr.
!
----- Method: SMarkRunner>>reportOn: (in category 'reporting') -----
reportOn: aStream
SMarkReporter defaultReporter reportFor: self on: aStream
!
----- Method: SMarkRunner>>results (in category 'accessing') -----
results
^ results!
----- Method: SMarkRunner>>runBaseBenchmark (in category 'benchmarking') -----
runBaseBenchmark
"In certain sitatuations it is one wants a baseline that is incooprated in all
benchmark results to be substracted from the final values.
#baseBenchmark can be used to charaterize such a baseline"
(suite respondsTo: #baseBenchmark)
ifFalse: [ ^ nil ].
^ self performBenchmark: #baseBenchmark.!
----- Method: SMarkRunner>>suite (in category 'accessing') -----
suite
^ suite!
----- Method: SMarkRunner>>suite: (in category 'accessing') -----
suite: aBenchmarkSuite
suite := aBenchmarkSuite.
suite runner: self.!
----- Method: SMarkRunner>>timedBenchmarkExecution: (in category 'benchmarking') -----
timedBenchmarkExecution: aSelector
"Will do a timed execution of the benchmark and return the result timer"
| timer result |
timers := Dictionary new.
timer := self createTimer: 'total'.
timer start.
result := suite perform: aSelector.
timer stop.
suite processResult: result withTimer: timer.
self recordResults: timer for: aSelector.
^ timer!
SMarkRunner subclass: #SMarkWeakScalingRunner
instanceVariableNames: 'numInnerIterations runningProcesses completionSignal runningProcessesMtx'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
----- Method: SMarkWeakScalingRunner class>>defaultNumberOfInnerIterations (in category 'defaults') -----
defaultNumberOfInnerIterations
"The number of iterations of the inner loop
in which the benchmark is executed."
^ 1!
----- Method: SMarkWeakScalingRunner>>execute:withProcesses:withTimer: (in category 'benchmarking') -----
execute: aSelector withProcesses: numberOfProcesses withTimer: timer
"This case is meant for all cases. REM: this is also used for numProc==1 to be able to measure the process start overhead in all cases.
It will start the processes and wait for their completion."
| processes |
processes := Array new: numberOfProcesses.
runningProcessesMtx := Semaphore forMutualExclusion.
completionSignal := Semaphore new.
runningProcesses := numberOfProcesses.
"First initialize the processes"
1 to: numberOfProcesses do: [ :procNum |
| proc |
proc := SMarkWeakScalingRunnerExecutor createFor: aSelector for: numInnerIterations with: self and: suite.
proc priority: Processor highestPriority.
proc name: (self class name, '-', procNum asString).
processes at: procNum put: proc.
"On: procNum"
].
"Now, execute the benchmark and do the timing now"
timer start.
1 to: numberOfProcesses do: [ :procNum |
(processes at: procNum) resume.
].
completionSignal wait.
timer stop.
!
----- Method: SMarkWeakScalingRunner>>executorCompleted (in category 'benchmarking') -----
executorCompleted
runningProcessesMtx critical: [
runningProcesses := runningProcesses - 1.
(runningProcesses == 0) ifTrue: [
completionSignal signal.
]
]!
----- Method: SMarkWeakScalingRunner>>initialize (in category 'initialization') -----
initialize
super initialize.
numProcesses := self class defaultNumberOfProcesses.
numInnerIterations := self class defaultNumberOfInnerIterations.
!
----- Method: SMarkWeakScalingRunner>>innerIterations (in category 'benchmarking') -----
innerIterations
"The number of inner iterations the benchmark is executed inside a processes"
^ numInnerIterations!
----- Method: SMarkWeakScalingRunner>>innerIterations: (in category 'benchmarking') -----
innerIterations: anInteger
"The number of inner iterations the benchmark is executed inside a processes"
numInnerIterations := anInteger!
----- Method: SMarkWeakScalingRunner>>processes (in category 'benchmarking') -----
processes
^ numProcesses!
----- Method: SMarkWeakScalingRunner>>processes: (in category 'benchmarking') -----
processes: anInteger
numProcesses := anInteger!
----- Method: SMarkWeakScalingRunner>>reportConfiguration: (in category 'reporting') -----
reportConfiguration: aStream
super reportConfiguration: aStream.
aStream << ('inner iterations: ', numInnerIterations asString); cr.!
----- Method: SMarkWeakScalingRunner>>timedBenchmarkExecution: (in category 'benchmarking') -----
timedBenchmarkExecution: aSelector
"Will do a timed execution of the benchmark and return the result timer"
| timer |
timers := Dictionary new.
timer := self createTimer: 'total'.
self execute: aSelector withProcesses: numProcesses withTimer: timer.
self recordResults: timer for: aSelector.
^ timer!
Object subclass: #SMarkSuite
instanceVariableNames: 'runner selectedBenchmarks'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkSuite commentStamp: '<historical>' prior: 0!
A Benchmark Suite is a set of benchmarks and it knows what exactly needs to be executed.
However, it does not really know how to execute it.
It knows all the magic, that is, how to set up and tear down the environment for the benchmarks, but does not have the knowledge of how many iterations need to be done and how to evaluate any results that might be produced.
Please see also SMarkHarness, which is the main class relevant for users to execute benchmarks with SMark.
Usage:
Choose a suite (i.e. one of my subclasses) and use the class-side #run or run: messages.
To get an example print the result of the following expression:
SMarkCompiler run: 10
SMarkLoops runOnly: #benchArrayAccess
!
SMarkSuite subclass: #SMarkDeltaBlue
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!SMarkDeltaBlue commentStamp: '<historical>' prior: 0!
One-way constraint solver Benchmark. The main focus in DeltaBlue is on polymorphism and object-oriented programming
To run the benchmark, execute the expression 'SMarkDeltaBlue run: 10'.!
----- Method: SMarkDeltaBlue>>benchDeltaBlue (in category 'benchmarking') -----
benchDeltaBlue
"This the combined benchmark."
| n |
n := self problemSize.
DBStrength initialize.
self doChain: n.
self doProjection: n!
----- Method: SMarkDeltaBlue>>defaultProblemSize (in category 'defaults') -----
defaultProblemSize
^ 20000!
----- Method: SMarkDeltaBlue>>doChain: (in category 'benchmarking') -----
doChain: n
| vars editConstraint plan planner |
planner := DBPlanner new.
vars := (1 to: n+1) collect: [ :i | DBVariable new].
"thread a chain of equality constraints through the variables"
1 to: n do:
[ :i || v1 v2 |
v1 := vars at: i.
v2 := vars at: i + 1.
DBEqualityConstraint var: v1 var: v2 strength: #required].
DBStayConstraint var: vars last strength: #strongDefault.
editConstraint := DBEditConstraint var: (vars first) strength: #preferred.
plan := planner extractPlanFromConstraints: (Array with: editConstraint).
1 to: 100 do: [ :v |
vars first value: v.
plan execute.
vars last value ~= v ifTrue: [self error: 'Chain test failed!!!!']].
editConstraint destroyConstraint!
----- Method: SMarkDeltaBlue>>doProjection: (in category 'benchmarking') -----
doProjection: n
"This test constructs a two sets of variables related to each other by
a simple linear transformation (scale and offset)."
| scale offset src dst planner dests |
planner := DBPlanner new.
dests := OrderedCollection new.
scale := DBVariable value: 10.
offset := DBVariable value: 1000.
1 to: n do:
[ :i |
src := DBVariable value: i.
dst := DBVariable value: i.
dests add: dst.
DBStayConstraint var: src strength: #default.
DBScaleConstraint var: src var: scale var: offset var: dst strength: #required].
planner changeVar: src newValue: 17.
dst value ~= 1170 ifTrue: [self error: 'Projection test 1 failed!!!!'].
planner changeVar: dst newValue: 1050.
src value ~= 5 ifTrue: [self error: 'Projection test 2 failed!!!!'].
planner changeVar: scale newValue: 5.
1 to: n - 1 do: [ :i |
(dests at: i) value ~= (i*5 + 1000)
ifTrue: [self error: 'Projection test 3 failed!!!!']].
planner changeVar: offset newValue: 2000.
1 to: n - 1 do: [ :i |
(dests at: i) value ~= (i*5 + 2000)
ifTrue: [self error: 'Projection test 4 failed!!!!']].!
----- Method: SMarkDeltaBlue>>problemSize (in category 'accessing') -----
problemSize
<omniUnenforced> "Hint for the OMOP that it is part of the meta infrastructure"
| ps |
ps := super problemSize.
ps isInteger ifFalse: [ ^ self defaultProblemSize].
^ ps!
SMarkSuite subclass: #SMarkRichards
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Richards'!
!SMarkRichards commentStamp: '<historical>' prior: 0!
Richards is an OS kernel simulation benchmark, originally written in BCPL by Martin Richards. The main focus in Richards is on property access and calling functions and methods.
To run the benchmark, execute the expression 'SMarkRichards run: 10'.!
----- Method: SMarkRichards>>benchRichards (in category 'benchs') -----
benchRichards
RichObject initialize.
self problemSize timesRepeat: [ RichRunner start ]!
----- Method: SMarkRichards>>defaultProblemSize (in category 'benchs') -----
defaultProblemSize
^ 50!
----- Method: SMarkRichards>>problemSize (in category 'benchs') -----
problemSize
<omniUnenforced> "Hint for the OMOP that it is part of the meta infrastructure"
| ps |
ps := super problemSize.
ps isInteger ifFalse: [ ^ self defaultProblemSize].
^ ps!
----- Method: SMarkSuite class>>defaultProblemSize (in category 'defaults') -----
defaultProblemSize
^ nil!
----- Method: SMarkSuite class>>defaultRunner (in category 'defaults') -----
defaultRunner
^ self onCog: [SMarkCogRunner]
else: [SMarkRunner]!
----- Method: SMarkSuite class>>isAbstractClass (in category 'benchmarking') -----
isAbstractClass
"This is a hack that is necessary in Squeak since it does not provide #isAbstractClass.
Actually this class is supposed to be abstract, but well, inheritance..."
^ false!
----- Method: SMarkSuite class>>onCog:else: (in category 'platform support') -----
onCog: cogSpecificBlock else: general
^ (Smalltalk vm isRunningCogit)
ifTrue: [cogSpecificBlock value]
ifFalse: [general value]!
----- Method: SMarkSuite class>>profile: (in category 'profiling') -----
profile: aSelector
^ self profileRunner
execute: self new selector: aSelector.!
----- Method: SMarkSuite class>>profile:iterations: (in category 'profiling') -----
profile: aSelector iterations: nIterations
^ self profileRunner
execute: self new selector: aSelector iterations: nIterations.!
----- Method: SMarkSuite class>>profileAll (in category 'profiling') -----
profileAll
^ self profileRunner
execute: self new.!
----- Method: SMarkSuite class>>profileRunner (in category 'profiling') -----
profileRunner
^ SMarkProfileRunner!
----- Method: SMarkSuite class>>run (in category 'benchmarking') -----
run
"Execute the suite one time."
^ self defaultRunner execute: self new.!
----- Method: SMarkSuite class>>run: (in category 'benchmarking') -----
run: nIterations
"Execute the suite a given number of iterations."
^ self defaultRunner execute: self new with: nIterations.!
----- Method: SMarkSuite class>>runOnly: (in category 'benchmarking') -----
runOnly: aSelector
"aSelector should refer to a benchmark method.
Example:
SMarkLoops runOnly: #benchFloatLoop
"
^ self defaultRunner execute: (self new runOnly: aSelector)!
----- Method: SMarkSuite class>>runOnly:iterations: (in category 'benchmarking') -----
runOnly: aSelector iterations: anInteger
"Execute only the bench name aSelector from the suite."
^ self defaultRunner execute: (self new runOnly: aSelector) with: anInteger!
----- Method: SMarkSuite>>benchmarkNameForSelector: (in category 'helper') -----
benchmarkNameForSelector: selector
"Extracts the actual name of the benchmark from the selector"
(selector beginsWith: #bench) ifTrue: [ ^ selector copyFrom: 6 to: selector size].
^ selector asSymbol
!
----- Method: SMarkSuite>>cleanUpInstanceVariables (in category 'running') -----
cleanUpInstanceVariables
"Make sure all variables that are 'user variables' get cleaned"
self class allInstVarNames do: [ :name |
name = 'runner' ifFalse: [
self instVarNamed: name put: nil ] ]!
----- Method: SMarkSuite>>performCustomSelector:with: (in category 'benchmarking') -----
performCustomSelector: aSelector with: aPrefix
| customSelector |
customSelector := (aPrefix, aSelector capitalized) asSymbol.
(self respondsTo: customSelector) ifTrue: [
self perform: customSelector].!
----- Method: SMarkSuite>>problemSize (in category 'benchmarking') -----
problemSize
<omniUnenforced> "Hint for the OMOP that it is part of the meta infrastructure"
runner ifNil: [^ self class defaultProblemSize].
runner problemSize ifNil: [^ self class defaultProblemSize].
^ runner problemSize!
----- Method: SMarkSuite>>processResult:withTimer: (in category 'benchmarking') -----
processResult: anObject withTimer: aSMarkTimer
"subclass responsability. You can verify your results here, or do things with the timer."
^self.!
----- Method: SMarkSuite>>run (in category 'benchmarking') -----
run
"Executes all the benchmarks in the suite,
coordinating with the runner when necessary"
| potentialBenchmarkSelectors |
selectedBenchmarks
ifNotNil: [ potentialBenchmarkSelectors := selectedBenchmarks ]
ifNil: [ potentialBenchmarkSelectors := self class allSelectors ].
potentialBenchmarkSelectors
do: [ :selector |
(self shouldRunSelector: selector)
ifTrue: [
runner performBenchmark: selector ]
].
!
----- Method: SMarkSuite>>runBenchmark: (in category 'benchmarking') -----
runBenchmark: aSelector
[self setUp.
self performCustomSelector: aSelector with: #setUp.
runner timedBenchmarkExecution: aSelector] ensure: [
self performCustomSelector: aSelector with: #tearDown.
self tearDown.
self cleanUpInstanceVariables]!
----- Method: SMarkSuite>>runOnly: (in category 'benchmarking') -----
runOnly: aSymbol
selectedBenchmarks := IdentitySet newFrom: { aSymbol }.!
----- Method: SMarkSuite>>runner (in category 'accessing') -----
runner
^ runner!
----- Method: SMarkSuite>>runner: (in category 'accessing') -----
runner: aRunner
runner := aRunner.!
----- Method: SMarkSuite>>selectedBenchmarks (in category 'benchmarking') -----
selectedBenchmarks
^ selectedBenchmarks!
----- Method: SMarkSuite>>setUp (in category 'running') -----
setUp
"It is the subclass' responsibility to set up the necessary environment for a benchmark"
^ self!
----- Method: SMarkSuite>>shouldRunSelector: (in category 'testing') -----
shouldRunSelector: selector
"Tells whether the given selector is in the form bench*, and thus is a benchmark that should be executed."
(selector includes: $:) ifTrue: [ ^ false ].
^ selector beginsWith: #bench!
----- Method: SMarkSuite>>tearDown (in category 'running') -----
tearDown
"It is the subclass' responsibility to clean up the environment after a benchmark"
^ self!
Object subclass: #SMarkTimer
instanceVariableNames: 'startTime elapsedTime name'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkTimer commentStamp: '<historical>' prior: 0!
A SMarkTimer is a simple timer.
A subclass can measure alternative metrics, or for instance use different time sources.
A subclass of SMarkRunner can then use the custom timer class by overriding SMarkRunner class >> #defaultTimer.!
----- Method: SMarkTimer class>>new: (in category 'instance creation') -----
new: aName
| timer |
timer := super new.
timer name: aName.
^timer!
----- Method: SMarkTimer>>currentMillis (in category 'timing') -----
currentMillis
^ Time millisecondClockValue!
----- Method: SMarkTimer>>initialize (in category 'initialization') -----
initialize
super initialize.
elapsedTime := 0!
----- Method: SMarkTimer>>name (in category 'accessing') -----
name
^name!
----- Method: SMarkTimer>>name: (in category 'accessing') -----
name: aString
name := aString !
----- Method: SMarkTimer>>reset (in category 'timing') -----
reset
startTime := 0.
elapsedTime := 0.!
----- Method: SMarkTimer>>start (in category 'timing') -----
start
startTime := self currentMillis.!
----- Method: SMarkTimer>>stop (in category 'timing') -----
stop
| elapsedInThisPeriod current |
current := self currentMillis.
elapsedInThisPeriod := Time milliseconds: current since: startTime.
elapsedTime := elapsedTime + elapsedInThisPeriod.!
----- Method: SMarkTimer>>totalTime (in category 'accessing') -----
totalTime
^elapsedTime!
Object subclass: #SMarkTransporter
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
!SMarkTransporter commentStamp: 'StefanMarr 4/5/2012 21:43' prior: 0!
SMarkTransporter is a used to interact with a git-fileout system used in the RoarVM project to manage Smalltalk source code.
SMarkTransporter is not actually a Transporter class, since there are currently no needs for customization.
Thus, it is just a dummy class for future use, and to hold #transportersForFileOutMenu.
!
----- Method: SMarkTransporter class>>transportersForFileOutMenu (in category 'transporter') -----
transportersForFileOutMenu
^ { (Smalltalk at: #Transporter ifAbsent: [^#()])
forPackage: (PackageInfo named: 'SMark') }!
Object subclass: #SMarkWeakScalingRunnerExecutor
instanceVariableNames: 'numInnerIterations benchmarkSelector suite runner'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-SMark'!
----- Method: SMarkWeakScalingRunnerExecutor class>>createFor:for:with:and: (in category 'as yet unclassified') -----
createFor: aSelector for: numIterations with: aRunner and: aSuite
| o |
o := self new.
o runner: aRunner.
o suite: aSuite.
o innerIterations: numIterations.
o benchmarkSelector: aSelector.
^ ([ o run ] newProcess)!
----- Method: SMarkWeakScalingRunnerExecutor>>benchmarkSelector: (in category 'accessing') -----
benchmarkSelector: aSelector
benchmarkSelector := aSelector!
----- Method: SMarkWeakScalingRunnerExecutor>>innerIterations: (in category 'accessing') -----
innerIterations: anInt
numInnerIterations := anInt!
----- Method: SMarkWeakScalingRunnerExecutor>>run (in category 'benchmarking') -----
run
1 to: numInnerIterations do: [:i |
suite perform: benchmarkSelector.].
runner executorCompleted.!
----- Method: SMarkWeakScalingRunnerExecutor>>runner: (in category 'accessing') -----
runner: aRunner
runner := aRunner!
----- Method: SMarkWeakScalingRunnerExecutor>>suite: (in category 'accessing') -----
suite: aSuite
suite := aSuite!
Object subclass: #ShootoutBody
instanceVariableNames: 'x y z vx vy vz mass'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutBody class>>daysPerYear (in category 'constants') -----
daysPerYear
^365.24!
----- Method: ShootoutBody class>>jupiter (in category 'constants') -----
jupiter
^self new
x: 4.84143144246472090
y: -1.16032004402742839
z: -1.03622044471123109e-1
vx: 1.66007664274403694e-3 * self daysPerYear
vy: 7.69901118419740425e-3 * self daysPerYear
vz: -6.90460016972063023e-5 * self daysPerYear
mass: 9.54791938424326609e-4 * self solarMass!
----- Method: ShootoutBody class>>neptune (in category 'constants') -----
neptune
^self new
x: 1.53796971148509165e1
y: -2.59193146099879641e1
z: 1.79258772950371181e-1
vx: 2.68067772490389322e-3 * self daysPerYear
vy: 1.62824170038242295e-3 * self daysPerYear
vz: -9.51592254519715870e-5 * self daysPerYear
mass: 5.15138902046611451e-5 * self solarMass!
----- Method: ShootoutBody class>>pi (in category 'constants') -----
pi
^3.141592653589793!
----- Method: ShootoutBody class>>saturn (in category 'constants') -----
saturn
^self new
x: 8.34336671824457987
y: 4.12479856412430479
z: -4.03523417114321381e-1
vx: -2.76742510726862411e-3 * self daysPerYear
vy: 4.99852801234917238e-3 * self daysPerYear
vz: 2.30417297573763929e-5 * self daysPerYear
mass: 2.85885980666130812e-4 * self solarMass!
----- Method: ShootoutBody class>>solarMass (in category 'constants') -----
solarMass
^4.0 * self pi * self pi!
----- Method: ShootoutBody class>>sun (in category 'constants') -----
sun
^self new
x: 0.0
y: 0.0
z: 0.0
vx: 0.0
vy: 0.0
vz: 0.0
mass: self solarMass!
----- Method: ShootoutBody class>>uranus (in category 'constants') -----
uranus
^self new
x: 1.28943695621391310e1
y: -1.51111514016986312e1
z: -2.23307578892655734e-1
vx: 2.96460137564761618e-3 * self daysPerYear
vy: 2.37847173959480950e-3 * self daysPerYear
vz: -2.96589568540237556e-5 * self daysPerYear
mass: 4.36624404335156298e-5 * self solarMass!
----- Method: ShootoutBody>>addMomentumTo: (in category 'nbody') -----
addMomentumTo: anArray
anArray at: 1 put: (anArray at: 1) + (vx * mass).
anArray at: 2 put: (anArray at: 2) + (vy * mass).
anArray at: 3 put: (anArray at: 3) + (vz * mass).
^anArray!
----- Method: ShootoutBody>>and:velocityAfter: (in category 'nbody') -----
and: aBody velocityAfter: dt
| dx dy dz distance mag |
dx := x - aBody x.
dy := y - aBody y.
dz := z - aBody z.
distance := ((dx*dx) + (dy*dy) + (dz*dz)) sqrt.
mag := dt / (distance * distance * distance).
self decreaseVelocity: dx y: dy z: dz m: aBody mass * mag.
aBody increaseVelocity: dx y: dy z: dz m: mass * mag!
----- Method: ShootoutBody>>decreaseVelocity:y:z:m: (in category 'nbody') -----
decreaseVelocity: dx y: dy z: dz m: m
vx := vx - (dx * m).
vy := vy - (dy * m).
vz := vz - (dz * m)!
----- Method: ShootoutBody>>increaseVelocity:y:z:m: (in category 'nbody') -----
increaseVelocity: dx y: dy z: dz m: m
vx := vx + (dx * m).
vy := vy + (dy * m).
vz := vz + (dz * m)!
----- Method: ShootoutBody>>kineticEnergy (in category 'nbody') -----
kineticEnergy
^0.5 * mass * ((vx * vx) + (vy * vy) + (vz * vz))!
----- Method: ShootoutBody>>mass (in category 'accessing') -----
mass
^mass!
----- Method: ShootoutBody>>offsetMomentum: (in category 'nbody') -----
offsetMomentum: anArray
| m |
m := self class solarMass.
vx := (anArray at: 1) negated / m.
vy := (anArray at: 2) negated / m.
vz := (anArray at: 3) negated / m!
----- Method: ShootoutBody>>positionAfter: (in category 'nbody') -----
positionAfter: dt
x := x + (dt * vx).
y := y + (dt * vy).
z := z + (dt * vz)!
----- Method: ShootoutBody>>potentialEnergy: (in category 'nbody') -----
potentialEnergy: aBody
| dx dy dz distance |
dx := x - aBody x.
dy := y - aBody y.
dz := z - aBody z.
distance := ((dx*dx) + (dy*dy) + (dz*dz)) sqrt.
^mass * aBody mass / distance!
----- Method: ShootoutBody>>x (in category 'accessing') -----
x
^x!
----- Method: ShootoutBody>>x:y:z:vx:vy:vz:mass: (in category 'accessing') -----
x: d1 y: d2 z: d3 vx: d4 vy: d5 vz: d6 mass: d7
x := d1.
y := d2.
z := d3.
vx := d4.
vy := d5.
vz := d6.
mass := d7!
----- Method: ShootoutBody>>y (in category 'accessing') -----
y
^y!
----- Method: ShootoutBody>>z (in category 'accessing') -----
z
^z!
Object subclass: #ShootoutChameneosColour
instanceVariableNames: 'color'
classVariableNames: 'Blue Red Yellow'
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutChameneosColour class>>blue (in category 'accessing') -----
blue
^Blue!
----- Method: ShootoutChameneosColour class>>blue: (in category 'accessing') -----
blue: anObject
Blue := anObject!
----- Method: ShootoutChameneosColour class>>createBlue (in category 'initialize-release') -----
createBlue
"comment stating purpose of message"
^super new color: #blue!
----- Method: ShootoutChameneosColour class>>createRed (in category 'initialize-release') -----
createRed
"comment stating purpose of message"
^super new color: #red!
----- Method: ShootoutChameneosColour class>>createYellow (in category 'initialize-release') -----
createYellow
"comment stating purpose of message"
^super new color: #yellow!
----- Method: ShootoutChameneosColour class>>generateReportOfColoursOn: (in category 'printing') -----
generateReportOfColoursOn: readOut
| colours |
colours := Array
with: Blue
with: Red
with: Yellow.
colours do:
[:aColour |
colours do:
[:anotherColour |
aColour printOn: readOut.
readOut nextPutAll: ' + '.
anotherColour printOn: readOut.
readOut nextPutAll: ' -> '.
(aColour complementaryColourFor: anotherColour) printOn: readOut.
readOut nl]].
^readOut!
----- Method: ShootoutChameneosColour class>>initialize (in category 'initialize-release') -----
initialize
"self initialize"
Red := self createRed.
Blue := self createBlue.
Yellow := self createYellow!
----- Method: ShootoutChameneosColour class>>red (in category 'accessing') -----
red
^Red!
----- Method: ShootoutChameneosColour class>>red: (in category 'accessing') -----
red: anObject
Red := anObject!
----- Method: ShootoutChameneosColour class>>yellow (in category 'accessing') -----
yellow
^Yellow!
----- Method: ShootoutChameneosColour class>>yellow: (in category 'accessing') -----
yellow: anObject
Yellow := anObject!
----- Method: ShootoutChameneosColour>>color (in category 'accessing') -----
color
^color!
----- Method: ShootoutChameneosColour>>color: (in category 'accessing') -----
color: aColor
color := aColor!
----- Method: ShootoutChameneosColour>>complementaryColourFor: (in category 'as yet unclassified') -----
complementaryColourFor: aChameneosColour
"determine the complementary colour defined as..."
self == aChameneosColour ifTrue: [^self].
self isBlue
ifTrue:
[aChameneosColour isRed
ifTrue: [^self class yellow]
ifFalse: [^self class red]].
self isRed
ifTrue:
[aChameneosColour isBlue
ifTrue: [^self class yellow]
ifFalse: [^self class blue]].
aChameneosColour isBlue
ifTrue: [^self class red]
ifFalse: [^self class blue]!
----- Method: ShootoutChameneosColour>>hasSameColorAs: (in category 'testing') -----
hasSameColorAs: aChameneos
^self color == aChameneos color!
----- Method: ShootoutChameneosColour>>isBlue (in category 'testing') -----
isBlue
^self == self class blue!
----- Method: ShootoutChameneosColour>>isRed (in category 'testing') -----
isRed
^self == self class red!
----- Method: ShootoutChameneosColour>>isYellow (in category 'testing') -----
isYellow
^self == self class yellow!
----- Method: ShootoutChameneosColour>>printOn: (in category 'printing') -----
printOn: aStream
aStream nextPutAll: self color!
Object subclass: #ShootoutCreature
instanceVariableNames: 'creatureName colour selfMet creaturesMet'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutCreature class>>withName:colour: (in category 'initialize-release') -----
withName: aName colour: aColour
^(ShootoutCreature new initialize)
name: aName;
colour: aColour!
----- Method: ShootoutCreature>>colour (in category 'accessing') -----
colour
^colour!
----- Method: ShootoutCreature>>colour: (in category 'accessing') -----
colour: anObject
colour := anObject!
----- Method: ShootoutCreature>>creaturesMet (in category 'accessing') -----
creaturesMet
^creaturesMet!
----- Method: ShootoutCreature>>creaturesMet: (in category 'accessing') -----
creaturesMet: anObject
creaturesMet := anObject!
----- Method: ShootoutCreature>>initialize (in category 'initialize-release') -----
initialize
selfMet := 0.
creaturesMet := 0!
----- Method: ShootoutCreature>>name (in category 'accessing') -----
name
^creatureName!
----- Method: ShootoutCreature>>name: (in category 'accessing') -----
name: anObject
creatureName := anObject!
----- Method: ShootoutCreature>>selfMet (in category 'accessing') -----
selfMet
^selfMet!
----- Method: ShootoutCreature>>selfMet: (in category 'accessing') -----
selfMet: anObject
^selfMet := anObject!
----- Method: ShootoutCreature>>visitMall: (in category 'controlling') -----
visitMall: mall
[| partner |
partner := mall visitWith: self.
partner ifNotNil:
[colour := colour complementaryColourFor: partner colour.
self == partner ifTrue: [selfMet := selfMet + 1].
creaturesMet := creaturesMet + 1].
partner isNil]
whileFalse!
Object subclass: #ShootoutMall
instanceVariableNames: 'guard maxRendezvous open process queue cache pairCache'
classVariableNames: 'Units'
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutMall class>>createAllowing: (in category 'initialize-release') -----
createAllowing: maxRendezvous
"Private"
^self basicNew initialize maxRendezvous: maxRendezvous!
----- Method: ShootoutMall class>>createCreaturesWith: (in category 'initialize-release') -----
createCreaturesWith: aCollectionOfColours
"Private"
| aName |
aName := 0.
^aCollectionOfColours collect:
[:aColour |
aName := aName + 1.
ShootoutCreature withName: aName colour: aColour]!
----- Method: ShootoutMall class>>generateReportFor:printOn: (in category 'printing') -----
generateReportFor: creatures printOn: stream
| sum |
sum := creatures inject: 0 into: [:accum :each | accum + each creaturesMet].
creatures do:
[:aCreature |
aCreature creaturesMet printOn: stream.
stream
space;
nextPutAll: (self units at: aCreature selfMet + 1);
nl].
stream space.
sum printString
do: [:el | stream nextPutAll: (self units at: el digitValue + 1)]
separatedBy: [stream space].
^stream!
----- Method: ShootoutMall class>>generateReportForColours:printOn: (in category 'printing') -----
generateReportForColours: colours printOn: stream
stream space.
colours do: [:colour | colour printOn: stream] separatedBy: [stream space].
^stream!
----- Method: ShootoutMall class>>initialize (in category 'initialize-release') -----
initialize
"self initialize"
Units := #('zero' 'one' 'two' 'three' 'four' 'five' 'six' 'seven' 'eight' 'nine')!
----- Method: ShootoutMall class>>new (in category 'initialize-release') -----
new
^self shouldNotImplement!
----- Method: ShootoutMall class>>openMall:forCreatures:usingGuard: (in category 'private') -----
openMall: aMall forCreatures: creatures usingGuard: sema
| processes |
processes := creatures
collect: [:aCreature |
[aCreature visitMall: aMall.
sema signal] newProcess].
processes do:
[:proc |
proc priority: Processor userBackgroundPriority.
proc resume]!
----- Method: ShootoutMall class>>openMallWith:forNumberOfMeets: (in category 'initialize-release') -----
openMallWith: aCollectionOfColours forNumberOfMeets: aNumber
| mall creatures guard |
mall := self createAllowing: aNumber.
mall run.
creatures := self createCreaturesWith: aCollectionOfColours.
guard := Semaphore new.
self
openMall: mall
forCreatures: creatures
usingGuard: guard.
self
waitForClosingOfMall: mall
withCreatures: creatures
usingGuard: guard.
^creatures!
----- Method: ShootoutMall class>>runBenchMark:on: (in category 'public') -----
runBenchMark: number on: anOutputStream
"self runBenchMark: 60000 on: Transcript."
| firstTestColours secondTestColours blue red yellow creatures |
blue := ShootoutChameneosColour blue.
red := ShootoutChameneosColour red.
yellow := ShootoutChameneosColour yellow.
firstTestColours := Array
with: blue
with: red
with: yellow.
secondTestColours := (OrderedCollection new)
add: blue;
add: red;
add: yellow;
add: red;
add: yellow;
add: blue;
add: red;
add: yellow;
add: red;
add: blue;
yourself.
(ShootoutChameneosColour generateReportOfColoursOn: anOutputStream) nl.
(self generateReportForColours: firstTestColours printOn: anOutputStream)
nl.
creatures := ShootoutMall openMallWith: firstTestColours forNumberOfMeets: number.
(self generateReportFor: creatures printOn: anOutputStream)
nl;
nl.
(self generateReportForColours: secondTestColours printOn: anOutputStream)
nl.
creatures := ShootoutMall openMallWith: secondTestColours forNumberOfMeets: number.
(self generateReportFor: creatures printOn: anOutputStream)
nl;
nl!
----- Method: ShootoutMall class>>units (in category 'accessing') -----
units
^Units!
----- Method: ShootoutMall class>>waitForClosingOfMall:withCreatures:usingGuard: (in category 'private') -----
waitForClosingOfMall: aMall withCreatures: creatures usingGuard: guard
creatures size timesRepeat: [guard wait].
aMall close!
----- Method: ShootoutMall>>close (in category 'controlling') -----
close
open := false!
----- Method: ShootoutMall>>initialize (in category 'initialize-release') -----
initialize
guard := Semaphore forMutualExclusion.
queue := SharedQueue new.
cache := OrderedCollection new.
1 to: 10 do: [:x | cache add: ShootoutPair new]!
----- Method: ShootoutMall>>maxRendezvous: (in category 'accessing') -----
maxRendezvous: max
maxRendezvous := max!
----- Method: ShootoutMall>>obtainPair (in category 'private') -----
obtainPair
^cache removeFirst!
----- Method: ShootoutMall>>processVisitors (in category 'private') -----
processVisitors
[open] whileTrue:
[1 to: maxRendezvous
do:
[:x |
| first second |
first := queue next.
second := queue next.
self setPartnersOn: first and: second.
first signal.
second signal].
[queue isEmpty] whileFalse: [queue next signal]].
process terminate.
process := nil!
----- Method: ShootoutMall>>releasePair: (in category 'private') -----
releasePair: pair
pair release.
cache addFirst: pair!
----- Method: ShootoutMall>>run (in category 'initialize-release') -----
run
open := true.
process ifNil:
[process := [self processVisitors] newProcess.
process priority: Processor userBackgroundPriority -1 ].
process resume!
----- Method: ShootoutMall>>setPartnersOn:and: (in category 'private') -----
setPartnersOn: first and: second
first partner: second me.
second partner: first me.
!
----- Method: ShootoutMall>>shutdown (in category 'private') -----
shutdown
[queue isEmpty] whileFalse: [queue next signal].
process terminate.
process := nil!
----- Method: ShootoutMall>>visitWith: (in category 'controlling') -----
visitWith: aChameneos
| pair partner |
pair := self obtainPair.
pair me: aChameneos.
queue nextPut: pair.
pair wait.
partner := pair partner.
self releasePair: pair.
^partner!
Object subclass: #ShootoutNBodySystem
instanceVariableNames: 'bodies'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutNBodySystem>>after: (in category 'nbody') -----
after: dt
1 to: bodies size do: [:i|
i+1 to: bodies size do: [:j|
(bodies at: i) and: (bodies at: j) velocityAfter: dt].
].
bodies do: [:each| each positionAfter: dt]!
----- Method: ShootoutNBodySystem>>energy (in category 'nbody') -----
energy
| e |
e := 0.0.
1 to: bodies size do: [:i|
e := e + (bodies at: i) kineticEnergy.
i+1 to: bodies size do: [:j|
e := e - ((bodies at: i) potentialEnergy: (bodies at: j))].
].
^e!
----- Method: ShootoutNBodySystem>>initialize (in category 'initialize-release') -----
initialize
bodies := (OrderedCollection new
add: ShootoutBody sun; add: ShootoutBody jupiter; add: ShootoutBody saturn;
add: ShootoutBody uranus; add: ShootoutBody neptune; yourself) asArray.
bodies first offsetMomentum:
(bodies inject: (Array with: 0.0 with: 0.0 with: 0.0)
into: [:m :each | each addMomentumTo: m])!
Object subclass: #ShootoutPair
instanceVariableNames: 'partner me sema'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutPair class>>new (in category 'instance creation') -----
new
"Answer a newly created and initialized instance."
^super new initialize.!
----- Method: ShootoutPair class>>with: (in category 'instance creation') -----
with: me
"Answer a newly created and initialized instance."
self halt.
^super new initialize me: me!
----- Method: ShootoutPair>>initialize (in category 'initialize-release') -----
initialize
"Initialize a newly created instance. This method must answer the receiver."
partner := nil.
me := nil.
sema := Semaphore new.
^self!
----- Method: ShootoutPair>>me (in category 'accessing') -----
me
^me!
----- Method: ShootoutPair>>me: (in category 'accessing') -----
me: anObject
me := anObject!
----- Method: ShootoutPair>>partner (in category 'accessing') -----
partner
^partner!
----- Method: ShootoutPair>>partner: (in category 'accessing') -----
partner: anObject
partner := anObject!
----- Method: ShootoutPair>>release (in category 'initialize-release') -----
release
partner:=nil.!
----- Method: ShootoutPair>>signal (in category 'initialize-release') -----
signal
sema signal!
----- Method: ShootoutPair>>wait (in category 'initialize-release') -----
wait
sema wait!
Object subclass: #ShootoutTests
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutTests class>>arg (in category 'platform') -----
arg
3 to: 5 do:
[:i|
(SmalltalkImage current getSystemAttribute: i) ifNotNil:
[:aString|
aString asInteger ifNotNil:
[:arg| ^arg]]].
^nil!
----- Method: ShootoutTests class>>binarytrees (in category 'benchmark scripts') -----
binarytrees
self binarytrees: self arg to: self stdout.
^''!
----- Method: ShootoutTests class>>binarytrees:to: (in category 'benchmarking') -----
binarytrees: n to: output
| minDepth maxDepth stretchDepth check longLivedTree iterations |
minDepth := 4.
maxDepth := minDepth + 2 max: n.
stretchDepth := maxDepth + 1.
check := (ShootoutTreeNode bottomUpTree: 0 depth: stretchDepth) itemCheck.
output
nextPutAll: 'stretch tree of depth '; print: stretchDepth; tab;
nextPutAll: ' check: '; print: check; nl.
longLivedTree := ShootoutTreeNode bottomUpTree: 0 depth: maxDepth.
minDepth to: maxDepth by: 2 do: [:depth|
iterations := 1 bitShift: maxDepth - depth + minDepth.
check := 0.
1 to: iterations do: [:i|
check := check + (ShootoutTreeNode bottomUpTree: i depth: depth) itemCheck.
check := check + (ShootoutTreeNode bottomUpTree: -1*i depth: depth) itemCheck
].
output
print: (2*iterations); tab;
nextPutAll: ' trees of depth '; print: depth; tab;
nextPutAll: ' check: '; print: check; nl
].
output
nextPutAll: 'long lived tree of depth '; print: maxDepth; tab;
nextPutAll: ' check: '; print: longLivedTree itemCheck; nl!
----- Method: ShootoutTests class>>chameneosredux2 (in category 'benchmark scripts') -----
chameneosredux2
self chameneosredux: self arg to: self stdout.
^''!
----- Method: ShootoutTests class>>chameneosredux:to: (in category 'benchmarking') -----
chameneosredux: arg to: aStream
ShootoutMall runBenchMark: arg on: aStream!
----- Method: ShootoutTests class>>collectReferenceTimes (in category 'benchmark scripts') -----
collectReferenceTimes
"Run the benchmarks 3 times and take their average, e.g. suitable
for filling in values for referenceTimesForClosureInterpreter"
"ShootoutTests collectReferenceTimes"
| n refs |
Transcript clear.
n := 3.
refs := (1 to: n) collect: [:i| ShootoutTests runAllToInternalStream].
^{ refs.
(1 to: refs first size) collect:
[:i|
((refs inject: 0 into: [:sum :ref| (ref at: i) + sum]) / n) rounded] }!
----- Method: ShootoutTests class>>nbody (in category 'benchmark scripts') -----
nbody
self nbody: self arg to: self stdout!
----- Method: ShootoutTests class>>nbody:to: (in category 'benchmarking') -----
nbody: count to: output
| bodies |
bodies := ShootoutNBodySystem new initialize.
output print: bodies energy digits: 9; cr.
count timesRepeat: [bodies after: 0.01].
output print: bodies energy digits: 9; cr.
^''!
----- Method: ShootoutTests class>>profileAll (in category 'profiling') -----
profileAll
"self profileAll"
| stream |
stream := DummyStream new.
self nbody: 200000 "20000000" to: stream.
self binarytrees: 15 to: stream.
self chameneosredux: 260000 to: stream.
self threadring: 10000000 to: stream!
----- Method: ShootoutTests class>>referenceArgs (in category 'benchmark scripts') -----
referenceArgs
^self referenceTimesAndArgsForClosureInterpreter collect: [:ea| ea last]!
----- Method: ShootoutTests class>>referenceTimesAndArgsForClosureInterpreter (in category 'benchmark scripts') -----
referenceTimesAndArgsForClosureInterpreter
"Interpreter + Closures VM (Mac Cocoa 5.7b3 27-Aug-10 >7BCAB029-A835-4D12-946D-4AB7083D2955< VMMaker versionString 4.4.9)
on Eliot's 2012 vintage 2.2GHz Intel (quad) Core i7 MacBook Pro"
^Dictionary new
at: #nbody put: #(40903 2000000);
at: #binarytrees put: #(30573 17);
at: #chameneosredux put: #(30722 2000000);
at: #threadring put: #(9148 30000000);
yourself!
----- Method: ShootoutTests class>>referenceTimesForClosureInterpreter (in category 'benchmark scripts') -----
referenceTimesForClosureInterpreter
^self referenceTimesAndArgsForClosureInterpreter collect: [:ea| ea first]!
----- Method: ShootoutTests class>>report:time:reference:on: (in category 'reporting') -----
report: name time: millisecs reference: reference on: aStream
aStream
cr;
nextPutAll: name; cr;
nextPutAll: ' took '; print: millisecs / 1000.0; nextPutAll: ' seconds'; cr; flush;
nextPutAll: 'ratio: '; print: ((millisecs / reference) roundTo: 0.001);
nextPutAll: ' % change: '; print: ((millisecs - reference * 100 / reference) roundTo: 0.01); nextPut: $%;
cr; flush!
----- Method: ShootoutTests class>>runAllToDummyStream (in category 'benchmark scripts') -----
runAllToDummyStream
"Transcript clear.
self runAllToDummyStream"
^self runAllToDummyStreamVs: self referenceTimesForClosureInterpreter!
----- Method: ShootoutTests class>>runAllToDummyStreamVs: (in category 'benchmark scripts') -----
runAllToDummyStreamVs: referenceTimes
"Transcript clear.
self runAllToDummyStreamVs: self referenceTimesForClosureInterpreter"
"Transcript clear.
self runAllToDummyStreamVs: self referenceTimesForSqueakVM"
^self runAllToDummyStreamVs: referenceTimes reportTo: Transcript!
----- Method: ShootoutTests class>>runAllToDummyStreamVs:reportTo: (in category 'benchmark scripts') -----
runAllToDummyStreamVs: referenceTimes reportTo: aStream
"Transcript clear.
self runAllToDummyStreamVs: self referenceTimesForClosureInterpreter"
"Transcript clear.
self runAllToDummyStreamVs: self referenceTimesForSqueakVM"
| times ratios geometricMean |
times := Array new writeStream.
ratios := Array new writeStream.
(self standardSuiteTo: DummyStream basicNew) do:
[:block | | benchmark reference t |
benchmark := (self selectorForSimpleBlock: block) copyUpTo: $:.
reference := referenceTimes at: benchmark asSymbol.
Smalltalk garbageCollect.
times nextPut: (t := Time millisecondsToRun: block).
ratios nextPut: t asFloat / reference.
self report: block decompile printString time: t reference: reference on: aStream].
geometricMean := (ratios contents inject: 1 into: [:m :n| m * n]) raisedTo: 1 / ratios position.
aStream
nextPutAll: 'geometric mean '; print: (geometricMean roundTo: 0.001);
nextPutAll: ' average speedup '; print: ((geometricMean - 1 * 100) roundTo: 0.01); nextPut: $%; cr; cr; flush.
^times contents!
----- Method: ShootoutTests class>>runAllToInternalStream (in category 'benchmark scripts') -----
runAllToInternalStream
"Transcript clear.
self runAllToInternalStream"
^self runAllToInternalStreamVs: self referenceTimesForClosureInterpreter!
----- Method: ShootoutTests class>>runAllToInternalStreamVs: (in category 'benchmark scripts') -----
runAllToInternalStreamVs: referenceTimes
"Transcript clear.
self runAllToInternalStreamVs: self referenceTimesForClosureInterpreter"
"Transcript clear.
self runAllToInternalStreamVs: self referenceTimesForSqueakVM"
| times ratios geometricMean |
times := Array new writeStream.
ratios := Array new writeStream.
(self standardSuiteTo: (ByteString new: 10000) writeStream) do:
[:block | | benchmark reference t |
benchmark := (self selectorForSimpleBlock: block) copyUpTo: $:.
reference := referenceTimes at: benchmark asSymbol.
Smalltalk garbageCollect.
times nextPut: (t := Time millisecondsToRun: block).
ratios nextPut: t asFloat / reference.
self report: block decompile printString time: t reference: reference on: Transcript].
geometricMean := (ratios contents inject: 1 into: [:m :n| m * n]) raisedTo: 1 / ratios position.
Transcript
nextPutAll: 'geometric mean '; print: (geometricMean roundTo: 0.001);
nextPutAll: ' average speedup '; print: ((geometricMean - 1 * 100) roundTo: 0.01); nextPut: $%; cr; cr; flush.
^times contents!
----- Method: ShootoutTests class>>runAllToTranscript (in category 'benchmark scripts') -----
runAllToTranscript
"Transcript clear.
self runAllToTranscript"
^self runAllToTranscriptVs: self referenceTimesForClosureInterpreter!
----- Method: ShootoutTests class>>runAllToTranscriptVs: (in category 'benchmark scripts') -----
runAllToTranscriptVs: referenceTimes
"Transcript clear.
self runAllToTranscriptVs: self referenceTimesForClosureInterpreter"
"Transcript clear.
self runAllToTranscriptVs: self referenceTimesForSqueakVM"
| times ratios geometricMean |
times := Array new writeStream.
ratios := Array new writeStream.
(self standardSuiteTo: Transcript) do:
[:block | | benchmark reference t |
benchmark := (self selectorForSimpleBlock: block) copyUpTo: $:.
reference := referenceTimes at: benchmark asSymbol.
Smalltalk garbageCollect.
times nextPut: (t := Time millisecondsToRun: block).
ratios nextPut: t asFloat / reference.
self report: block decompile printString time: t reference: reference on: Transcript].
geometricMean := (ratios contents inject: 1 into: [:m :n| m * n]) raisedTo: 1 / ratios position.
Transcript
nextPutAll: 'geometric mean '; print: (geometricMean roundTo: 0.001);
nextPutAll: ' average speedup '; print: ((geometricMean - 1 * 100) roundTo: 0.01); nextPut: $%; cr; cr; flush.
^times contents!
----- Method: ShootoutTests class>>selectorForSimpleBlock: (in category 'benchmark scripts') -----
selectorForSimpleBlock: aBlock
| is |
is := InstructionStream on: aBlock method.
is pc: aBlock startpc.
is scanFor:
[:x| | selectorOrScanner |
(selectorOrScanner := is selectorToSendOrSelf) ~~ is ifTrue:
[^selectorOrScanner].
false].
^nil!
----- Method: ShootoutTests class>>standardSuiteTo: (in category 'benchmark scripts') -----
standardSuiteTo: aStream
"revised up from
{ [self nbody: 200000 to: stream].
[self binarytrees: 15 to: stream].
[self chameneosredux: 260000 to: stream].
[self threadring: 10000000 to: stream] }
on 6/15/2014"
| reference nbodyCount binaryTreeDepth chameneosCount threadringCount |
reference := self referenceTimesAndArgsForClosureInterpreter.
nbodyCount := (reference at: #nbody) last.
binaryTreeDepth := (reference at: #binarytrees) last.
chameneosCount := (reference at: #chameneosredux) last.
threadringCount := (reference at: #threadring) last.
^{ [self nbody: nbodyCount to: aStream].
[self binarytrees: binaryTreeDepth to: aStream].
[self chameneosredux: chameneosCount to: aStream].
[self threadring: threadringCount to: aStream] }!
----- Method: ShootoutTests class>>stdin (in category 'platform') -----
stdin
^StandardFileStream stdIn!
----- Method: ShootoutTests class>>stdout (in category 'platform') -----
stdout
^StandardFileStream stdout!
----- Method: ShootoutTests class>>threadRing:output: (in category 'benchmarking') -----
threadRing: aSemaphore output: output
| first last |
503 to: 1 by: -1 do: [:i|
first := ShootoutThread named: i next: first done: aSemaphore output: output.
last isNil ifTrue: [ last := first ].
].
last nextThread: first.
^first !
----- Method: ShootoutTests class>>threadring (in category 'benchmark scripts') -----
threadring
self threadring: self arg to: self stdout.
^''!
----- Method: ShootoutTests class>>threadring:to: (in category 'benchmarking') -----
threadring: arg to: output
| done |
(self threadRing: (done := Semaphore new) output: output) takeToken: arg.
done wait!
Object subclass: #ShootoutThread
instanceVariableNames: 'name nextThread token semaphore done output'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutThread class>>named:next:done:output: (in category 'instance creation') -----
named: anInteger next: aThread done: aSemaphore output: aStream
^self new name: anInteger; nextThread: aThread; done: aSemaphore; output: aStream; fork !
----- Method: ShootoutThread class>>new (in category 'instance creation') -----
new
^self basicNew semaphore: Semaphore new !
----- Method: ShootoutThread>>done: (in category 'accessing') -----
done: aSemaphore
done := aSemaphore !
----- Method: ShootoutThread>>fork (in category 'accessing') -----
fork
[ self run ] fork !
----- Method: ShootoutThread>>name: (in category 'accessing') -----
name: anInteger
name := anInteger !
----- Method: ShootoutThread>>nextThread: (in category 'accessing') -----
nextThread: aThread
nextThread := aThread !
----- Method: ShootoutThread>>output: (in category 'accessing') -----
output: anObject
"Set the value of output"
output := anObject!
----- Method: ShootoutThread>>run (in category 'accessing') -----
run
[ self tokenNotDone ] whileTrue: [ nextThread takeToken: token - 1 ].
output print: name.
output name = 'stdout'
ifTrue: [output nl]
ifFalse: [output cr; flush].
done signal !
----- Method: ShootoutThread>>semaphore: (in category 'accessing') -----
semaphore: aSemaphore
semaphore := aSemaphore !
----- Method: ShootoutThread>>takeToken: (in category 'accessing') -----
takeToken: x
token := x.
semaphore signal !
----- Method: ShootoutThread>>tokenNotDone (in category 'accessing') -----
tokenNotDone
semaphore wait.
^token > 0 !
Object subclass: #ShootoutTreeNode
instanceVariableNames: 'left right item'
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-Shootout'!
----- Method: ShootoutTreeNode class>>bottomUpTree:depth: (in category 'instance creation') -----
bottomUpTree: anItem depth: anInteger
^(anInteger > 0)
ifTrue: [
self
left: (self bottomUpTree: 2*anItem - 1 depth: anInteger - 1)
right: (self bottomUpTree: 2*anItem depth: anInteger - 1)
item: anItem
]
ifFalse: [self left: nil right: nil item: anItem]!
----- Method: ShootoutTreeNode class>>left:right:item: (in category 'instance creation') -----
left: leftChild right: rightChild item: anItem
^(super new) left: leftChild right: rightChild item: anItem!
----- Method: ShootoutTreeNode>>itemCheck (in category 'accessing') -----
itemCheck
^left isNil
ifTrue: [item] ifFalse: [item + (left itemCheck - right itemCheck)]!
----- Method: ShootoutTreeNode>>left:right:item: (in category 'initialize-release') -----
left: leftChild right: rightChild item: anItem
left := leftChild.
right := rightChild.
item := anItem!
----- Method: DummyStream>>cr (in category '*CogBenchmarks-Shootout-platform') -----
cr!
----- Method: DummyStream>>nl (in category '*CogBenchmarks-Shootout-platform') -----
nl
"do nothing"!
----- Method: DummyStream>>print:digits: (in category '*CogBenchmarks-Shootout-platform') -----
print: number digits: decimalPlaces
"do nothing"!
----- Method: DummyStream>>print:paddedTo: (in category '*CogBenchmarks-Shootout-platform') -----
print: number paddedTo: width
"do nothing"!
----- Method: DummyStream>>space (in category '*CogBenchmarks-Shootout-platform') -----
space!
----- Method: DummyStream>>tab (in category '*CogBenchmarks-Shootout-platform') -----
tab!
OrderedCollection subclass: #DBPlan
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'CogBenchmarks-DeltaBlue'!
!DBPlan commentStamp: '<historical>' prior: 0!
A Plan is an ordered list of constraints to be executed in sequence to resatisfy all currently satisfiable constraints in the face of one or more changing inputs.!
----- Method: DBPlan>>execute (in category 'planning') -----
execute
"Execute my constraints in order."
self do: [: c | c execute].!
----- Method: Stream>>nl (in category '*CogBenchmarks-Shootout-platform') -----
nl
self nextPut: Character lf!
----- Method: Stream>>print:digits: (in category '*CogBenchmarks-Shootout-platform') -----
print: number digits: decimalPlaces
| precision rounded |
decimalPlaces <= 0 ifTrue: [^ number rounded printString].
precision := Utilities floatPrecisionForDecimalPlaces: decimalPlaces.
rounded := number roundTo: precision.
self nextPutAll:
((rounded asScaledDecimal: decimalPlaces) printString copyUpTo: $s)!
----- Method: Stream>>print:paddedTo: (in category '*CogBenchmarks-Shootout-platform') -----
print: number paddedTo: width
self nextPutAll: (number printStringLength: width padded: false)!
More information about the Vm-dev
mailing list