[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