Smalltalk80LanguageImplementationKor:Chapter 23
- 제 23 장 이벤트 구동 시뮬레이션에서 통계 수집하기
이벤트 주도 시뮬레이션에서 통계 수집하기
이벤트 주도 시뮬레이션을 명시하기 위한 프레임워크는 앞장에서 소개하였다. 이 프레임워크는 시뮬레이션이 실행되는 동안 그에 관한 통계를 수집하는 데 사용되는 메서드는 포함하지 않았다. 통계는 각 시뮬레이션 객체가 시뮬레이션에서 소모하는 시간량(그에 따라 객체가 명시한 작업 유형의 실행을 모델이 얼마나 잘 지원하는지), 큐의 길이에 대한 정보(예: 각 큐에서 소모되는 시간량이나 최대, 최소, 평균 길이), 그리고 시뮬레이션의 자원 활용에 관한 정보로 구성된다.
Simulation 클래스 또는 SimulationObject 클래스에 통계 수집을 추가하는 방법에는 다수가 있다. 이번 장에서는 네 가지 통계 수집 예제를 제공할 것인데, 바로 기간 통계, 처리량 히스토그램, 이벤트 기록(tally), 이벤트 감시가 그것들이다. 각 예제는 데이터를 저장할 수 있는 사전이나 데이터를 작성할 수 있는 파일을 제공하기 위해 Simulation 또는 SimulationObject의 서브클래스를 생성하고, 적절한 데이터를 저장하기 위해 적절한 메서드를 수정하는 과정을 수반한다.
기간 통계(Duration Statistics)
통계 수집의 첫 번째 예로 객체가 시뮬레이션으로 들어갔다가 나오는 시간을 단순히 저장하는 Simulation의 일반 서브클래스를 생성하겠다. 객체가 시뮬레이션으로 들어가는 시간과 (entranceTime) 시뮬레이션에서 소비한 시간을 (duration) 필드로 가진 특수 레코드에 시간을 기록한다. 기록은 다음과 같이 설명된다.
클래스명 | SimulationObjectRecord |
슈퍼클래스 | Object |
인스턴스 변수명 | entranceTime duration |
인스턴스 메서드 | accessing
entrance: currentTime
entranceTime ← currentTime
exit: currentTime
duration ← entranceTime - currentTime
entrance
↑entranceTime
exit
↑entranceTime + duration
duration
↑duration
printing
printOn: aStream
entranceTime printOn: aStream.
aStream tab.
duration printOn: aStream
|
통계를 수집하는 데 SimulationObjectRecords를 사용하는 Simulation의 서브클래스 예제로 StatisticsWithSimulation을 들 수 있다. 이는 아래와 같이 정의된다.
클래스명 | StatisticsWithSimulation |
슈퍼클래스 | Simulation |
인스턴스 변수명 | statistics |
인스턴스 메서드 | initialization
initialize
super initialize.
statistics ← Dictionary new
simulation scheduling
enter: anObject
statistics at: anObject
put: (SimulationObjectRecord new entrance: currentTime)
exit: anObject
(statistics at: anObject) exit: currentTime
statistics
printStatisticsOn: aStream
| stat |
aStream cr.
aStream nextPutAll: 'Object'.
aStream tab.
aStream nextPutAll: 'Entrance Time'.
aStream tab.
aStream nextPutAll: 'Duration'.
aStream cr.
"Sort with respect to the time the object entered the simulation. Because the keys as well as the values are needed, it is necessary to first obtain the set of associations and then sort them."
stat ← SortedCollection
sortBlock: [ :i :j | i value entrance < = j value entrance].
statistics associationsDo: [ :each | stat add: each].
stat do:
[ :anAssociation |
aStream cr.
anAssociation key printOn: aStream.
aStream tab.
anAssociation value printOn: aStream
"The value is a SimulationObjectRecord which prints the entrance time and duration"]
|
StatisticsWithSimulation의 서브클래스로 NothingAtAll을 생성했다고 가정하자. 이번 예제에서 NothingAtAll은 두 가지 시뮬레이션 객체를 스케줄링하는데, 하나는 아무 일도 하지 않는 객체로 (DoNothing), 0부터 시작해 3부터 5까지 시간 단위의 균일 분포에 따라 도착하고, 나머지 하나는 시뮬레이트 시간 5 단위 동안 둘러보는데 (Visitor) 1부터 시작해 5부터 10까지 단위의 균일 분포에 따라 도착한다. 이러한 객체들 중 하나가 시뮬레이션에 들어올 때마다 statistics 사전에 엔트리가 늘어난다. 키는 객체 자체이고, equals(=) 시험이 기본(==)이므로 각 객체가 유일한 키가 된다. 키는 입력 데이터가 초기화된 SimulationObjectRecord의 인스턴스다. 객체가 나가면 그와 연관된 레코드가 복구되어 종료 데이터(exit data)를 설정하기 위해 그곳으로 메시지가 전송된다.
클래스명 | DoNothing |
슈퍼클래스 | SimulationObject |
인스턴스 메서드 | no new methods
|
클래스명 | Visitor |
슈퍼클래스 | SimulationObject |
인스턴스 메서드 | simulation control
tasks
self holdFor: 5
|
클래스명 | NothingAtAll |
슈퍼클래스 | StatisticsWithSimulation |
인스턴스 메서드 | initialization
defineArrivalSchedule
self scheduleArrivalOf: DoNothing
accordingTo: (Uniform from: 3 to: 5).
self scheduleArrivalOf: Visitor
accordingTo: (Uniform from: 5 to: 10)
startingAt: 1
|
시뮬레이션을 중단할 때마다 printStatisticsOn: aFile 메시지를 전송할 수 있다 (aFile은 일종의 FileStream이다). 결과는 아래와 같은 모습을 할 것이다.
객체 | 진입 시간 | 기간 |
a DoNothing | 0.0 | 0.0 |
a Visitor | 1.0 | 5.0 |
a DoNothing | 4.58728 | 0.0 |
a Visitor | 6.71938 | 5.0 |
a DoNothing | 9.3493 | 0.0 |
a DoNothing | 13.9047 | 0.0 |
a Visitor | 16.7068 | 5.0 |
a DoNothing | 17.1963 | 0.0 |
a DoNothing | 21.7292 | 0.0 |
a Visitor | 23.2563 | 5.0 |
a DoNothing | 25.6805 | 0.0 |
a DoNothing | 29.3202 | 0.0 |
a Visitor | 32.1147 | 5.0 |
a DoNothing | 32.686 | 0.0 |
a DoNothing | 36.698 | 0.0 |
a DoNothing | 41.1135 | 0.0 |
a Visitor | 41.1614 | 5.0 |
a DoNothing | 44.3258 | 0.0 |
a Visitor | 48.4145 | 5.0 |
a DoNothing | 48.492 | 0.0 |
a DoNothing | 51.7833 | 0.0 |
a Visitor | 53.5166 | 5.0 |
a DoNothing | 56.4262 | 0.0 |
a DoNothing | 60.5357 | 0.0 |
a Visitor | 63.4532 | 5.0 |
a DoNothing | 64.8572 | 0.0 |
a DoNothing | 68.7634 | 0.0 |
a Visitor | 68.921 | 5.0 |
a DoNothing | 72.4788 | 0.0 |
a DoNothing | 75.8567 | 0.0 |
처리량 히스토그램(Throughput Histograms)
시뮬레이션에서 수집된 공통적 통계는 객체의 처리량, 즉 특정 시간 내에 얼마나 많은 객체가 시뮬레이션을 통과해갔는지를 나타내고, 이는 시뮬레이션에서 각 객체가 소모하는 시간과 비례한다. 그러한 통계의 수집은 사전에 결정된 시간 간격 내에 시간을 소모한 객체의 개수를 추적하는 과정을 수반한다. 결과의 보고에는 객체의 개수, 최소 시간, 최대 시간, 각 명시된 시간 간격에 해당하는 객체의 개수를 표시하는 과정이 수반된다. 그러한 통계는 특히 요청을 처리하는 데 자원이 충분한지 결정하기 위한 자원을 수반하는 시뮬레이션에서 유용한데, 객체가 자원을 얻기 위해 오랜 시간 기다려야 한다면 시뮬레이션에서 더 많은 시간을 보내야 한다.
처리량 통계의 수집을 지원하기 위해 우리는 Histogram 클래스를 제공한다. Histograms는 사전에 명시된 범위 내의 값에 대한 기록을 유지한다. 가령, 다양한 값들이 1부터 5, 5부터 10, 10부터 20, 20부터 25, 25부터 30, 30부터 35, 35부터 40, 40부터 45까지 범위에 해당하는 횟수를 기록할 수 있겠다. 가령 5에서 45까지 간격을 크기 5의 bin으로 나누고 값이 각 bin에 저장되는 횟수의 실행 계수를 유지할 수 있겠다.
Histogram 클래스는 하계(lower bound), 상계(upper bound), bin 크기를 명시하여 생성된다. 위의 예제에서 Histogram을 얻으려면 아래를 평가한다.
Histogram from: 5 to: 45 by: 5
Bins에 있는 데이터 외에도 Histogram은 입력한 최소, 최대, 총 값을 기록한다. 엔트리가 간격(interval)의 경계 안에 들어가지 않을 수도 있는데, 그러한 엔트리를 저장하기 위해서는 추가 변수가 사용된다 (extraEntries). Bins는 배열의 요소로서 저장되고, 배열의 크기는 bin의 개수와 동일하다 (즉, 상계 - 하계 // bin 크기). store: aValue 메시지를 이용해 Histogram에 값을 넣는다. 증가되어야 하는 배열 요소의 색인은 1+ 이다 (aValue - 하계 // bin 크기). 표시된 메서드 대부분은 수집된 정보의 출력을 지원한다.
클래스명 | Histogram |
슈퍼클래스 | Object |
인스턴스 변수명 | tallyArray lowerBound upperBound step minValue maxValue totalValues extraEntries |
클래스 매서드 | class initialization
from: lowerNum to: upperNum by: step
↑self new newLower: lowerNum upper: upperNum by: step
|
인스턴스 메서드 | accessing
contains: aValue
↑lowerBound < = aValue and: [aValue < upperBound]
store: aValue
| index |
minValue isNil
ifTrue: [minValue ← maxValue ← aValue]
ifFalse: [minValue ← minValue min: aValue.
maxValue ← maxValue max: aValue].
totalValues ← totalValues + aValue.
(self contains: aValue)
ifTrue: [index ← (aValue - lowerBound // step) + 1.
tallyArray at: index put: (tallyArray at: index) + 1]
ifFalse: [extraEntries ← extraEntries + 1]
printing
printStatisticsOn: aStream
| totalObjs pos |
self firstHeader: aStream.
aStream cr; tab.
totalObjs ← extraEntries.
"count the number of entries the throughput records know"
tallyArray do: [ :each | totalObjs ← totalObjs + each].
totalObjs printOn: aStream.
aStream tab.
minValue printOn: aStream.
aStream tab.
maxValue printOn: aStream.
aStream tab.
(totalValues / totalObjs) asFloat printOn: aStream.
aStream cr.
self secondHeader: aStream.
aStream cr.
pos ← lowerBound.
tallyArray do:
[ :entry |
pos printOn: aStream.
aStream nextPut: $-.
(pos ← pos + step) printOn: aStream.
aStream tab.
entry printOn: aStream.
aStream tab.
(entry / totalObjs) asFloat printOn: aStream.
aStream tab.
aStream nextPut: $|
"print the X's"
entry rounded timesRepeat: [aStream nextPut: $X].
aStream cr]
firstHeader: aStream
aStream cr; tab.
aStream nextPutAll: 'Number of'.
aStream tab.
aStream nextPutAll: 'Minimum'.
aStream tab.
aStream nextPutAll: 'Maximum'.
aStream tab.
aStream nextPutAll: 'Average'.
aStream cr; tab.
aStream nextPutAll: 'Objects'.
aStream tab.
aStream nextPutAll: 'Value'.
aStream tab.
aStream nextPutAll: 'Value'.
aStream tab.
aStream nextPutAll: 'Value'
secondHeader: aStream
aStream cr; tab.
aStream nextPutAll: 'Number of'.
aStream cr.
aStream nextPutAll: 'Entry'.
aStream tab.
aStream nextPutAll: 'Objects'.
aStream tab.
aStream nextPutAll: 'Frequency'.
private
newLower: lowerNum upper: upperNum by: stepAmount
tallyArray ← Array new: (upperNum - lowerNum // stepAmount).
tallyArray atAllPut: 0.
lowerBound ← lowerNum.
upperBound ← upperNum.
step ← stepAmount.
minValue ← maxValue ← nil.
totalValues ← 0.
extraEntries ← 0
|
박물관에 방문하는 사람들의 시뮬레이션은 Histogram의 좋은 사용 예가 된다. Museum은 NothingAtAll과 같이 Visitors가 도착하고 둘러보는 장소가 된다. Visitors는 박물관에 전시된 유물 중에서 관심이 있는 대상에 따라 다양한 시간대로 구경한다. 이 시간은 평균 20과 표준 편차 5로 정규 분포된 것으로 가정한다. Visitors는 낮 시간에, 그리고 시뮬레이트된 시간으로 5부터 10 단위마다 한 명씩 박물관을 방문한다.
클래스명 | Museum |
슈퍼클래스 | Simulation |
인스턴스 변수명 | statistics |
인스턴스 메서드 | initialization
initialize
super initialize.
statistics ← Histogram from: 5 to: 45 by: 5
defineArrivalSchedule
self scheduleArrivalOf: Visitor
accordingTo: (Uniform from: 5 to: 10)
scheduling
exit: aSimulationObject
super exit: aSimulationObject.
statistics store: currentTime - aSimulationObject entryTime
printStatisticsOn: aStream
statistics printStatisticsOn: aStream
|
Museum 클래스에서 통계를 업데이트하기 위해서는 Visitors가 언제 박물관을 들어가는지 추적하고 입장 시간에 따른 질의에 응답할 수 있어야 한다.
클래스명 | Visitor |
슈퍼클래스 | SimulationObject |
인스턴스 변수명 | entryTime |
인스턴스 메서드 | initialization
initialize
super initialize.
entryTime ← ActiveSimulation time
accessing
entryTime
↑entryTime
simulation control
tasks
self holdFor: (Normal mean: 20 deviation: 5) next
|
시뮬레이션을 생성하고 실행하기 위해 아래를 평가한다.
aSimulation ← Museum new startUp.
[aSimulation time < 50] whileTrue: [aSimulation proceed]
Museum이 생성될 때 통계에 대한 Histogram이 생성되었다. Visitor가 Museum을 떠날 때마다 Histogram에 데이터가 저장된다. 데이터는 방문 지속시간으로 구성된다.
시간이 50이 될 때까지 시뮬레이션이 실행되고 나면 Museum에게 보고서를 요청한다.
aSimulation printStatisticsOn: (Disk file: 'museum.report')
printStatisticsOn: 과 연관된 메서드는 Histogram으로 동일한 메시지, printStatisticsOn: 을 전송하고, 이 메시지는 museum.report 파일에 아래의 정보를 출력한다.
객체 수 | 최소값 | 최대값 | 평균값 |
64 | 10.0202 | 31.2791 | 20.152 |
Entry | Number of Objects | Frequency | |
5-10 | 0 | 0 | |
10-15 | 14 | 0.21875 | XXXXXXXXXXXXXX |
15-20 | 16 | 0.25 | XXXXXXXXXXXXXXXX |
20-25 | 20 | 0.3125 | XXXXXXXXXXXXXXXXXXXX |
25-30 | 13 | 0.203125 | XXXXXXXXXXXXX |
30-35 | 1 | 0.015625 | X |
35-40 | 0 | 0 | |
40-45 | 0 | 0 |
이벤트 기록(Tallying Events)
시뮬레이션에서 이벤트를 기록하는 또 다른 예로 자주 사용되는 Traffic 시뮬레이션 예제를 제시하겠다. 이 시뮬레이션에서는 교차로에 진입하는 승용차의 수를 기록하여 교차로에서 직진하는 차량, 좌측으로 가는 차량, 우측으로 가는 차량을 각각 구별한다. 관찰을 통해 좌측이나 우측으로 가는 차량보다 직진하는 차량의 수가 약 2배에 달했지만 우측보다는 좌측으로 가는 차량이 2배에 달한다는 사실을 발견했다. 새로운 승용차는 시간 단위 0.5에서 2마다 균일 분포에 따라 교차로에 도착했다 (self scheduleArrivalOf: Car accordingTo: (Uniform from: 0.5 to: 2)). 시뮬레이트된 시간이 100을 넘을 때까지 시뮬레이션을 실행할 것이다.
클래스명 | Traffic |
슈퍼클래스 | Simulation |
인스턴스 변수명 | statistics |
인스턴스 메서드 | initialization
initialize
super initialize.
statistics ← Dictionary new: 3.
statistics at: #straight put: 0.
statistics at: #right put: 0.
statistics at: #left put: 0
defineArrivalSchedule
self scheduleArrivalOf: Car accordingTo: (Uniform from: 0.5 to: 2).
self schedule: [self finishUp] at: 100
statistics
update: key
statistics at: key put: (statistics at: key) + 1
printStatisticsOn: aStream
aStream cr.
aStream nextPutAll: 'Car Direction Tally'
statistics associationsDo:
[ :assoc |
aStream cr.
assoc key printOn: aStream.
aStream tab.
assoc value printOn: aStream]
|
defineArrivalSchedule과 연관된 메서드에서 시뮬레이트된 시간 100에 액션 self finishUp이 발생하도록 스케줄링되었음을 주목한다. 이 이벤트는 원하는 대로 시뮬레이션을 중단시킬 것이다.
클래스명 | Car |
슈퍼클래스 | SimulationObject |
인스턴스 메서드 | simulation control
tasks
"Sample, without replacement, the direction through the intersection that the car will travel."
| sample |
sample ← SampleSpace data:
#(left left right straight straight straight straight straight straight)
ActiveSimulation update: sample next
|
SampleSpace는 21장에서 소개한 적이 있는 클래스다. Cars는 시뮬레이션에게 알리기 위한 방향을 고르는 유일한 작업으로 구성된 시뮬레이션에 들어가도록 스케줄링된다. 시뮬레이션을 실행한 후에 우리는 Traffic으로 printStatisticsOn: 메시지를 전송함으로써 기록(tallies)을 보고할 것을 요청한다. 아래를 평가 시 가능한 결과는
aSimulation ← Traffic new startUp.
[aSimulation proceed] whileTrue.
aSimulation printStatisticsOn: (Disk file: 'traffic.data')
traffic.data 파일에 쓰인 아래의 정보가 된다.
승용차 방향 | 기록 |
straight | 57 |
right | 8 |
left | 15 |
이벤트 감시
시뮬레이션으로부터 데이터를 수집하는 또 다른 기법으로, 시뮬레이션 객체가 들어가고 나오는 것을 포함하여 각 (주요) 이벤트의 발생에 주목하는 방법이 있다. 이는 EventMonitor라 부르는 SimulationObject의 서브클래스를 생성하여 이루어진다. 이번 예제에서 클래스 변수는 그들의 이벤트에 관한 표기법을 저장할 수 있는 파일을 참조한다. 감시할 이벤트를 나타내는 각 메시지는 파일에 정보를 저장하기 위한 명령을 포함하도록 서브클래스에서 오버라이드되어야 한다. 슈퍼클래스 내 메서드는 여전히 의사 변수 super로 메시지를 분포하여 실행된다.
기본적으로 표기법은 수신자의 식별 및 시간(SimulationObject 종류), 그리고 "enters"(들어가다) 또는 "requests"(요청하다) 또는 "release"(방출하다)와 같은 표기법으로 구성된다.
클래스명 | EventMonitor |
슈퍼클래스 | SimulationObject |
클래스 변수명 | DataFile |
클래스 메서드 | class initialization
file: aFile
DataFile ← aFile
|
인스턴스 메서드 | scheduling
startUp
self timeStamp.
DataFile nextPutAll: 'enters'.
super startUp
finishUp
super finishUp.
self timeStamp.
DataFile nextPutAll: 'exits'
task language
holdFor: aTimeDelay
self timeStamp.
DataFile nextPutAll: 'holds for'.
aTimeDelay printOn: DataFile.
super holdFor: aTimeDelay
acquire: amount ofResource: resourceName
| aStaticResource |
"Store fact that resource is being requested."
self timeStamp.
DataFile nextPutAll: 'requests'.
amount printOn: DataFile.
DataFile nextPutAll: 'of', resourceName.
"Now try to get the resource."
aStaticResource ← super acquire: amount
ofResource: resourceName.
"Returns here when resource is obtained; store the fact."
self timeStamp.
DataFile nextPutAll: 'obtained'.
amount printOn: DataFile.
DataFile nextPutAll: 'of ', resourceName.
↑aStaticResource
acquire: amount ofResource: resourceName withPriority: priorityNumber
| aStaticResource |
"Store fact that resource is being requested"
self timeStamp.
DataFile nextPutAll: 'requests'.
amount printOn: DataFile.
DataFile nextPutAll: 'at priority'.
priorityNumber printOn: DataFile.
DataFile nextPutAll: 'of', resourceName.
"Now try to get the resource."
aStaticResource ←
super acquire amount
ofResource: resourceName
withPriority: priorityNumber.
"Returns here when resource is obtained; store the fact."
self timeStamp.
DataFile nextPutAll: "obtained'.
amount printOn: DataFile.
DataFile nextPutAll: 'of', resourceName.
↑aStaticResource
produce: amount ofResource: resourceName
self timeStamp.
DataFile nextPutAll: 'produces'
amount printOn: DataFile.
DataFile nextPutAll: 'of', resourceName.
super produce amount ofResource resourceName
release: aStaticResource
self timeStamp.
DataFile nextPutAll: 'releases'
aStaticResource amount printOn: DataFile.
DataFile nextPutAll: 'of', aStaticResource name.
super release: aStaticResource
acquireResource: resourceName
| anEvent |
"Store fact that resource is being requested"
self timeStamp.
DataFile nextPutAll: 'wants to serve for'.
DataFile nextPutAll: resourceName.
"Now try to get the resource."
anEvent ← super acquireResource resourceName.
"Returns here when resource is obtained store the fact."
self timeStamp.
DataFile nextPutAll: 'can serve"
anEvent condition printOn: DataFile.
↑anEvent
produceResource: resourceName
self timeStamp.
DataFile nextPutAll: 'wants to get service as'
DataFile nextPutAll: resourceName.
super produce amount ofResource resourceName
resume: anEvent
self timeStamp.
DataFile nextPutAll: 'resumes'.
anEvent condition printOn: DataFile.
super resume: anEvent
private
timeStamp
DataFile cr.
ActiveSimulation time printOn: DataFile.
DataFile tab.
self printOn: DataFile.
|
기본 시뮬레이션(DoNothings)과 Visitors의 도착으로 구성된 NothingAtAll 시뮬레이션의 이벤트를 감시하는 수도 있다. Visitor와 DoNothing을 SimulationObject 대신 EventMonitor의 서브클래스로 생성하는 것 외의 클래스 정의는 모두 동일하다.
클래스명 | DoNothing |
슈퍼클래스 | EventMonitor |
인스턴스 메서드 | no new methods
|
클래스명 | Visitor |
슈퍼클래스 | EventMonitor |
인스턴스 메서드 | simulation control
tasks
self holdFor: (Uniform from: 4.0 to: 10.0) next
|
NothingAtAll이 재정의되어 기본 시뮬레이션은 EventMonitor가 된다.
class name | NothingAtAll |
superclass | Simulation |
instance methods | initialization
defineArrivalSchedule
self scheduleArrivalOf: DoNothing
accordingTo: (Uniform from: 1 to: 5).
self scheduleArrivalOf: Visitor
accordingTo: (Uniform from: 4 to: 8)
startingAt: 3
|
아래를 실행하고 나면,
Visitor file: (Disk file: 'NothingAtAll.events').
"This informs DoNothing too"
aSimulation ← NothingAtAll new startUp.
[aSimulation time < 25] whileTrue: [aSimulation proceed]
'NothingAtAll.events' 파일은 아래의 정보를 포함한다.
0.0 | a DoNothing enters |
0.0 | a DoNothing exits |
3.0 | a Visitor enters |
3.0 | a Visitor holds for 7.5885 |
4.32703 | a DoNothing enters |
4.32703 | a DoNothing exits |
7.74896 | a Visitor enters |
7.74896 | a Visitor holds for 4.14163 |
8.20233 | a DoNothing enters |
8.20233 | a DoNothing exits |
10.5885 | a Visitor exits |
11.8906 | a Visitor exits |
12.5153 | a DoNothing enters |
12.5153 | a DoNothing exits |
14.2642 | a Visitor enters |
14.2642 | a Visitor holds for 4.51334 |
16.6951 | a DoNothing enters |
16.6951 | a DoNothing exits |
18.7776 | a Visitor exits |
19.8544 | a Visitor enters |
19.8544 | a Visitor holds for 5.10907 |
20.5342 | a DoNothing enters |
20.5342 | a DoNothing exits |
23.464 | a DoNothing enters |
23.464 | a DoNothing exits |
24.9635 | a Visitor exits |
도착하는 SimulationObject마다 다르게 이름을 붙인다면 이벤트의 순서를 따르는 기능이 향상된다. 목표는 NothingAtAll 시뮬레이션의 실행 trace가 다음과 같이 되도록 만드는 데 있다.
0.0 | DoNothing 1 enters |
0.0 | DoNothing 1 exits |
3.0 | Visitor 1 enters |
3.0 | Visitor 1 holds for 7.5885 |
4.32703 | DoNothing 2 enters |
4.32703 | DoNothing 2 exits |
7.74896 | Visitor 2 enters |
7.74896 | Visitor 2 holds for 4.14163 |
8.20233 | DoNothing 3 enters |
8.20233 | DoNothing 3 exits |
10.5885 | Visitor 1 exits |
11.8906 | Visitor 2 exits |
12.5153 | DoNothing 4 enters |
12.5153 | DoNothing 4 exits |
14.2642 | Visitor 3 enters |
14.2642 | Visitor 3 holds for 4.51334 |
16.6951 | DoNothing 5 enters |
16.6951 | DoNothing 5 exits |
18.7776 | Visitor 3 exits |
19.8544 | Visitor 4 enters |
19.8544 | Visitor 4 holds for 5.10907 |
20.5342 | DoNothing 6 enters |
20.5342 | DoNothing 6 exits |
23.464 | DoNothing 7 enters |
23.464 | DoNothing 7 exits |
24.9635 | Visitor 4 exits |
EventMonitor의 각 서브클래스는 고유의 라벨 시퀀스를 생성해야 한다. EventMonitor는 서브클래스가 그 인스턴스를 구별하는 방법을 구현하는 라벨 프레임워크를 설정한다. 서브클래스가 중복할 수 있는 모델을 EventMonitor 자체가 제공하므로, EventMonitor의 인스턴스를 사용하는 시뮬레이션을 이용해 위에 표시된 trace를 생성할 수 있다. 앞서 EventMonitor의 구현에서 표시한 scheduling, task language, private 메시지 외에도 클래스 설명은 아래와 같은 메시지를 갖고 있다.
클래스명 | EventMonitor |
슈퍼클래스 | SimulationObject |
인스턴스 변수명 | label |
클래스 변수명 | DataFile Counter |
클래스 메서드 | class initialization
file: aFile
DataFile ← aFile.
Counter ← 0
|
인스턴스 메서드 | initialization
initialize
super initialize.
self setLabel
accessing
setLabel
Counter ← Counter + 1.
label ← Counter printString
label
↑label
printing
printOn: aStream
self class name printOn: aStream.
aStream space.
aStream nextPutAll: self label
|
EventMonitor의 서브클래스로서 Visitor는 그 인스턴스들의 카운터 역할을 하는 독립적 클래스 변수를 갖고 있어야 한다. Visitor의 클래스 설명은 이제 아래와 같다.
클래스명 | Visitor |
슈퍼클래스 | EventMonitor |
클래스 변수명 | MyCounter |
클래스 메서드 | class initialization
file: aFile
super file: aFile.
MyCounter ← 0
|
인스턴스 메서드 | accessing
setLabel
MyCounter ← MyCounter + 1.
label ← MyCounter printString
simulation control
tasks
self holdFor: (Uniform from: 4.0 to: 10.0) next
|
인스턴스로 initialize를 알리면 MyCounter는 0으로 설정되며, 메서드는 EventMOnitor에서 발견된다. 출력(printing)은 MyCounter와 관련해 Visitor가 재정의하는 라벨을 검색한다. DoNothing이 그 슈퍼클래스의 클래스 변수, Counter를 사용하도록 둔다. 그러면 이러한 정의를 이용해 원하는 trace가 생성될 것이다.