Arrival generator: 사람들이 랜덤하게 극장에 도착하는 random interval을 생성하는 제너레이터
Ticket counter as a queueing system: 사람들이 티켓을 사는 큐잉 시스템
Popcorn stand as a queueing system: 사람들이 팝콘을 사는 큐잉 시스템
System Time 0
각 서버는 random number generator를 가지고 있다.
팝콘을 사거나 사지 않는 Case가 나뉘어져 있다.
초기 system time은 0로 시작.
Arrival Server에서는 현재 시점에서 얼마 뒤에 첫 번째 customer가 오는 지, 즉 inter-arrival time을 랜덤하게 발생시킨다.
Ticket counter 큐잉 시스템과 popcorn counter 큐잉 시스템은 customer가 와야 event가 발생하므로 아무런 변화가 없다.
System Time 0
Arrival server의 random number generator가 3이라는 inter-arrival time을 발생시켰다. (Box = Random number)
Completion Time(Event 완수 시간): 현재 시점(0)부터 3이라는 시간이 지난 3에 Arrival Event가 발생한다.
System Time 3
3이라는 시간에 Customer(1)가 극장에 Arrived하는 Event가 발생하므로 System Time은 가장 가까운 Event가 발생하는 시점인 3으로 업데이트된다. 이후 각 서버에서 다시 random number generator가 inter-arrival time을 발생시킨다.
Arrival server에서는 2라는 inter-arrival time을 발생시킨다. Customer(2)의 극장 도착시간은 현재 System time + inter-arrival time = 3 + 2 = 5이다.
SimPy: Discrete-event simulation library in Python
이전에 설명한 Active component는 모두 process(코드)가 구성한다.
모든 process는 Environment에 있어야 한다. Env 안에서 Event 간의 상호작용이 일어난다.
process는 yield라는 명령어를 통해 발생시킨다. yield event로 명시한 event가 발생하기 전까지는 clock의 업데이트나 다른 event의 발생이 이뤄지지 않는다. yield에 의해 event가 발생하면 그 다음 event로 넘어가게 된다.
Timeout이라는 event type도 중요하다. event와 event 사이 delay를 형성할 수 있다. 시간이 지나면 Timeout이라는 event를 발생시킨다. 시간이 걸려서 무언가가 일어나는 형태의 시뮬레이션에서 timeout을 많이 쓴다.
Timeout와 Env 내부의 event들은 Environment.timeout() or Environment.event()이라는 문법을 통해서 발생시킬 수 있다.
예제 1. Car() process
import simpy
# define car() process
def car(env):
while True:
print('Start parking at %d' % env.now)
parking_duration = 5
yield env.timeout(parking_duration)
print('Start driving at %d' % env.now)
trip_duration = 2
yield env.timeout(trip_duration)
env = simpy.Environment()
env.process(car(env))
env.run(until=30)
Start parking at 0
Start driving at 5
Start parking at 7
Start driving at 12
Start parking at 14
Start driving at 19
Start parking at 21
Start driving at 26
Start parking at 28
간단한 Discrete-event simulation을 진행한다.
car()라는 함수를 정의한다. 주차 시간은 5, 주행 시간은 2이다.
env.now=0에서 시작한다. 첫 번째 print문을 실행한다.
yield env.timeout(parking_duration): 주차 시간인 5라는 시간이 지나야 timeout event가 발생한다.
5라는 시간이 지나고 timeout이 발생한 뒤 두 번째 print문을 실행한다. env.now=5이다.
trip_duration=2이므로 yield env.timeout(trip_duration) 문에 의해 2가 지난 시점인 5+2=7에서 timout event가 발생한다. (driving event가 종료된다.)
이것을 반복하는 함수가 car()이다.
객체를 만드는 순서
import simpy: simpy 라이브러리를 불러오는 구문 (필수)
env = simpy.Environment(): simpy의 환경을 정의하는 구문 (필수)
env.process(car(env)): Env에 car라는 process를 등록하는 과정이다.
env.run(until=30): clock이 30이 될 때까지만 simulation을 진행한다.
예제 2. Process Interaction 1
Process는 Environment.process() 문을 통해 활성화된다.
2가지 활용 예
다른 프로세스가 끝날 때까지 기다리는 경우
event가 일어나는 동안 interrupt로 다른 프로세스를 일으키는 경우
SimPy Process는 event처럼 활용될 수 있다.
yield env.process(my_proc()) 문을 통해 my_proc()이라는 프로세스를 실행할 수 있다. 그러면 my_proc()가 끝나고 나서야 코드의 다음 문장이 실행된다.
import simpy
class Car(object):
def __init__(self, env):
self.env = env
# Start the run process everytime an instance is created.
self.proc = env.process(self.run())
def charge(self, duration):
yield self.env.timeout(duration)
def run(self):
while True:
print('Start parking and charging at %d' % env.now)
charge_duration = 5
# We yield the process that process() returns
# to wait for it to finish
yield env.process(self.charge(charge_duration))
# The charge process has finished and
# we can start driving again.
print('Start driving at %d' % env.now)
trip_duration = 2
yield env.timeout(trip_duration)
env = simpy.Environment()
car = Car(env)
env.run(until=30)
Start parking and charging at 0
Start driving at 5
Start parking and charging at 7
Start driving at 12
Start parking and charging at 14
Start driving at 19
Start parking and charging at 21
Start driving at 26
Start parking and charging at 28
Car(object)라는 클래스를 만든다.
__init__
파이썬에서 클래스의 메서드의 첫 번째 인자는 self이다. self는 객체의 인스턴스 그 자체를 말한다. 클래스 내부의 각 함수는 self를 매개변수로 받아야 한다. (self는 객체 자신을 참조하는 매개변수, 관련 문법은 구글링..)
__init__은 클래스의 생성자 구문이다. Car라는 클래스에 env를 매개변수로 받는다.
env.process(self.run()) 구문을 통해 run 프로세스를 Env에 등록한다.
charge(self, duration)
charge는 단순히 duration이라는 시간이 흐르도록 만든 함수이다.
run(self)
5초 간은 충전, 2초 간은 driving을 하도록 하는 함수이다.
env.now=0으로 시작하고, 첫 번째 print 문을 실행한다.
yield env.process(self.charge(charge_duration)) : 5라는 충전 시간이 주어졌고, charge는 timeout event를 5라는 시간에 발생시킨다. 즉, 5라는 시간에서 충전이 완료되고 출발을 시작한다.
env.now=5인 상태에서 두 번째 print 문을 실행한다.
trip_duration=2로 주행 시간은 2이다. timout은 2라는 시간이 지난 후인 7에서 발생한다. 따라서 7부터는 다시 5초 동안 충전을 하는 event가 발생하는 시점인 12로 넘어가게 된다.
이러한 과정을 반복한다.
아래 부분
env = simpy.Environment(): simpy의 환경을 정의하는 구문 (필수)
car = Car(env): Car라는 class의 객체를 만드는 구문. 이것을 적으면 env를 첫 번째 매개변수로 받아서 Car class의 __init__ 구문이 실행된다.
env.run(until=30): 30이라는 시간까지 simulation이 진행된다.
예제 3. Process Interaction 2
event가 실행되는 도중에 interrupt를 발생시킬 수 있다.
class Car(object): # Object 안 적어도 상관없음
def __init__(self, env):
self.env = env
# Start the run process everytime an instance is created.
self.action = env.process(self.run())
def run(self):
while True:
print('Start parking and charging at %d' % env.now)
charge_duration = 5
try:
yield env.process(self.charge(charge_duration))
except simpy.Interrupt:
print('Was interrupted. Hope, the battery is full enough ...')
print('Start driving at %d' % env.now)
trip_duration = 2
yield env.timeout(trip_duration)
def charge(self, duration):
yield self.env.timeout(duration)
def driver(env, car):
yield env.timeout(3)
car.action.interrupt()
env = simpy.Environment()
car = Car(env)
env.process(driver(env, car))
env.run(until=15)
Start parking and charging at 0
Was interrupted. Hope, the battery is full enough ...
Start driving at 3
Start parking and charging at 5
Start driving at 10
Start parking and charging at 12
예제 1의 코드를 그대로 사용하고 run() 함수만 일부 수정하고, driver() 함수를 추가한다.
run(self)
다른 부분을 예제 2와 동일하나 try, except문이 추가되었다.
평소에는 try문을 실행하지만 simpy.Interrupt가 발생하면 expect에 정의된 구문을 실행한다.
driver(env, car)
인터럽트를 걸기 위해 새로 만든 함수이다.
env와 car를 매개변수로 받는다.
timeout의 길이를 3으로 지정하였다. 3이 지나면 timeout event가 발생한다.
timeout이 발생하고 나면 바로 다음 구문이 실행된다. car를 매개변수로 받았기 때문에 car.action.interrupt() 문을 통해 car에 인터럽트 action을 발생시킬 수 있다.
아래 부분
env.process(driver(env,car)) 부분만 추가되었다. Car라는 프로세스가 진행되다가 3이라는 시점에서 driver process의 interrupt가 발생하게 된다. 전체 실행 중에서 딱 3에서만 한 번 인터럽트가 발생함에 유의하자.
예제 4. Shared Resources - Resources
SimPy는 모델을 만들 때 사용할 수 있는 3가지 유형의 자원을 제공한다.
Resources: 한정된 프로세스 수에 의해 사용되는 자원. 큐잉 시스템의 서버와 같은 역할을 한다. (ex. a gas station with a limited number of fuel pumps). 이것에 관한 예제만 다루겠다.
Containers: producer가 생성하는 resource와 consumer가 사용하는 resource가 동일(homogenous)한 경우
Stores: producer가 생성하는 resource와 consumer가 사용하는 resource가 다른(heterogeneous)한 경우
producer: resource를 만드는 그룹
consumer: producer가 생성한 resource를 사용하는 그룹
Resource
Resource의 타입
Resource: 가장 기본적인 타입의 자원
PriorityResource: Client마다 priority가 있어서 늦게 와도 우선적으로 서비스를 받을 수 있는 자원
PreemptiveResource: Priority보다 더 강력한 자원. 먼저 서비스 받고 있던 것마저 뺏고 서비스를 받는다.
def car(env, name, bcs, driving_time, charge_duration):
# Simulate driving to the BCS
yield env.timeout(driving_time)
# Request one of its charging spots
print('%s arriving at %d' % (name, env.now))
with bcs.request() as req:
yield req
print('%s starting to charge at %s' % (name, env.now))
yield env.timeout(charge_duration)
print('%s leaving the bcs at %s' % (name, env.now))
env = simpy.Environment()
bcs = simpy.Resource(env, capacity=2)
for i in range(4):
env.process(car(env, 'Car %d' % i, bcs, i*2, 5))
env.run()
Car 0 arriving at 0
Car 0 starting to charge at 0
Car 1 arriving at 2
Car 1 starting to charge at 2
Car 2 arriving at 4
Car 0 leaving the bcs at 5
Car 2 starting to charge at 5
Car 3 arriving at 6
Car 1 leaving the bcs at 7
Car 3 starting to charge at 7
Car 2 leaving the bcs at 10
Car 3 leaving the bcs at 12
Battery charging station(BCS)까지 차를 운전하고 charge를 한 후 떠나는 시뮬레이션이다.
위 예제에서 client(car)들은 2의 수용 용량을 갖는 battery charge station Resource에 서비스를 받고 싶다는 request()를 요청하게 된다.
car() 함수
env, name, bcs, driving_time, charge_duration을 매개변수로 받는다.
yield env.timeout(driving_time): 우선 driving_time 동안 주행을 시작한다.
timeout event가 발생하면 name과 timeout이 발생한 시점인 env.now를 함께 출력한다.
with bcs.request() as req: bcs라는 Resource에 서비스 요청을 하기 위해 사용하는 구문
이후: Resource에 서비스를 받는다는 요청을 하고, charge_duration에 맞춰 충전과 떠남을 알리는 구문을 출력한다.
만약 client들이 모두 resource를 사용하고 있다면 후순위로 도착한 client들은 큐잉 시스템처럼 자기차례가 될 때까지 기다린다.
아래 부분
bcs = simpy.Resource(env, capacity=2): 자원의 용량은 2이다. 최대 2개의 client만 서비스를 받을 수 있다.
for문: 총 4대의 car 프로세스를 생성한다. 2초의 inter-arrival time과 5의 충전시간을 갖는다. 4대의 car가 오는 큐잉시스템이 정의된다.