Smalltalk80LanguageImplementationKor:Chapter 23

From 흡혈양파의 번역工房
Jump to: navigation, search
제 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가 생성될 것이다.


Notes