wn42
코딩이랑 이것저것
wn42
전체 방문자
오늘
어제
  • 분류 전체보기 (113)
    • 프로그래머스 (23)
      • LV1 (11)
      • LV2 (1)
      • LV3 (3)
      • 연습 (8)
    • 딥러닝 공부 (0)
      • 머신러닝&딥러닝 이론 (0)
    • 임베디드 (17)
      • Adventure Design (1)
      • 센서기반모바일로봇 (5)
      • ROS (9)
      • Google Coral (2)
    • C++ (38)
      • C++ 기초 (34)
      • 자료구조 및 알고리즘 (4)
    • Python (14)
      • 기본 파이썬 문법 (6)
      • Python 기초 (8)
    • 빅데이터 (9)
      • 빅데이터 첫걸음 시작하기(국비지원) (5)
      • 빅데이터 공부 (4)
    • 알고리즘 공부 (2)
      • 기본 알고리즘 (2)
    • 전자공학 (10)
      • 반도체 공정 (3)
      • 무선데이터통신 (7)
      • 반도체공학 (0)
    • C# (0)
      • C# 기본 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 변수
  • 반복문
  • 데이터분석
  • 큐
  • 상속
  • 스택
  • 파이썬
  • 클래스
  • stl
  • Queue
  • 정렬
  • 빅데이터 첫걸음 시작하기
  • 노드
  • 빅데이터
  • c++
  • 데이터분석 인강
  • 바이트디그리
  • 패스트캠퍼스
  • 인스턴스
  • 내일배움카드
  • google coral
  • 스택/큐
  • 소멸자
  • ROS
  • 조건문
  • Python
  • K디지털크레딧
  • numpy
  • 딥러닝
  • 프로그래머스

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
wn42

코딩이랑 이것저것

Discrete-Event Simulation Using Simpy
전자공학/무선데이터통신

Discrete-Event Simulation Using Simpy

2022. 4. 8. 00:05

통신 이론의 Discrete-event를 가능하게 하는 파이썬 라이브러리는 SimPy가 유일하다.

Discrete-event Simulation

  • Discrete-event simulation: 세상은 event(사건)과 event의 상호작용으로 이루어진다.
    • System state는 a set of states로 표현된다.
    • Transition in states는 하나의 event에 의해 발생된다.
    • event가 발생할 때마다 the clock이 업데이트된다.
  • 다른 시뮬레이션들과 다른 점
    • event는 특정 순간에 즉시 일어난다.
    • 시간은 고정된 양으로 늘어나는 것이 아니라 사건이 발생하면 그 때 사건의 발생 시점으로 넘어간다.
    • 다음 event의 시간을 찾는 Polling이 수행된다.

 

State transition diagram

Discrete-event simulation의 상태 천이도: state의 transition은 event에 의해 발생한다. 천이하면서 동시에 action을 발생시킨다.

 

t1이라는 시간에 Event가 발생하여 상태천이(S1 -> S3)가 일어난다.

Event가 발생하지 않으면 state 천이가 일어나지 않는다.

 

  • Discrete-Event Simulation의 구성요소
    • Clock: Discrete-event simulation의 clock은 Event가 발생할 때마다 시간이 업데이트된다.
    • Events List: 현재 시간에서 가장 가까운 시간 순대로 Event를 담은 List
    • Random-Number Generators
    • Statics
    • Ending Condition

 

Ex) Movie Theater Simulation

  • Theater의 구성요소: People(customer), ticket counter(서버, 큐잉시스템), popcorn stand(서버, 큐잉시스템)
  • 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이다.
  • 티켓 큐잉 시스템에서는 3이라는 inter-arrival time을 발생시켰다. Customer(1)가 티켓 구매를 완료하는 Event는 3 + 3 = 6에 발생한다.
  • 팝콘 큐잉 시스템에는 아직 Customer가 없다.

 

System Time 5

  • 가장 가까운 Event인 Customer(2)의 도착 시간인 5로 System Time이 업데이트된다.
  • 아직 Customer(1)의 티켓 구매 완료 Event가 발생하는 시간인 6 이전이므로 Customer(2)는 큐에 대기한다.
  • Arrival Server에서 Customer(3)의 inter-arrival time을 5로 생성했다. 따라서 Customer(3)의 극장 도착 시간은 5 + 5 = 10이 되겠다.
  • 아직 팝콘 큐잉 시스템에는 Customer가 도착하지 않았다.

 

System Time 6

  • 현 시점에서 가장 가까운 Event인 Customer(1)의 티켓 구매 완료가 일어나는 시점인 6으로 System time이 업데이트된다.
  • Arrival server에서는 아직 Customer(3)의 도착 Event 시간이 되지 않았다.
  • 티켓 큐잉 시스템에서는 Customer(2)의 티켓 구매 완료 Event의 inter-arrival time을 3으로 랜덤 생성했다. 따라서 6 + 3 = 9라는 시점에서 Customer(2)는 팝콘을 구매하러 떠난다.
  • 팝콘 큐잉 시스템에 드디어 Customer(1)이 도착(구매하기로 함=1)했다. Customer(1)의 팝콘 구매 완료 Event의 inter-arrival time은 4로 랜덤 생성되었다. 따라서 현시점 + 4 = 6 + 4 = 10에서 팝콘 구매가 완료된다.
  • Polling은 다음 Event는 9에서 일어난다고 가리킨다.

 

System Time 9

  • Customer(2)가 티켓 구매를 완료하는 Event가 가장 가까운 event이므로 System Time은 9로 업데이트된다.
  • Customer(2)는 팝콘을 구매하지 않기로 하였다. (0)
  • 아직 Customer(3)의 도착과 Customer(1)의 팝콘 구매 완료 Event는 발생하지 않았다.

 

System Time 10

  • 예시에서는 Time=10이라는 시점에서 Customer(3)의 도착과 Customer(1)의 팝콘 구매 완료 Event가 발생할 것이라고 했지만 실제로 완전히 똑같은 시간 T=10에 Event가 동시에 발생할 확률은 0에 가깝다.
  • 그래서 Customer(1)의 팝콘 구매 Event가 먼저 발생하였다. System Time은 10으로 업데이트된다.

 

System Time 10

  • System Time=10인 시점에 Customer(3)의 도착 Event도 발생한다. Customer(3)이 티켓 구매 큐잉 시스템으로 이동하였다.
  • Arrival server에서는 Customer(4)의 inter-arrival Time을 2로 랜덤 생성했다. 따라서 현시점 + 2 = 10 + 2 = 12에서 Customer(4)의 도착 Event가 실행된다.
  • 티켓 구매 큐잉 시스템에서는 Customer(3)의 티켓 구매 완료 Event의 inter-arrival time을 4로 랜덤 생성했다. 10 + 4 = 14라는 시점에서 티켓 구매 완료 Event가 일어난다.
  • 이런 과정이 계속 반복되는 것이 Discrete-event simulation이다.

 

Simpy를 이용한 Discrete-event Simulation

  • 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가 오는 큐잉시스템이 정의된다.

 

더보기

(이부분은 아직 이해가 잘되지 않아서 다시 작성할 예정)

예제 5. Events 1

SimPy에는 다양한 목적의 이벤트 타입을 갖는 Event 집합이 내재되어 있다.

  • Event들은 다음과 같은 상태를 갖는다.
    • might happen (not triggered): 발생 전
    • is going to happen (triggerd): 발생 중
    • has happend (processed): 완료
  • event가 발생하면 event의 발생 시간과 evnet가 SimPy의 event queue에 삽입된다. 그리고 Event.triggered가 True로 활성화된다.

 

Callbacks

  • 이벤트가 발생하면 수행하는 함수리스트로 Event.callbacks 리스트에 이벤트를 추가할 수 있다.
  • 이벤트가 실행되면 이벤트를 큐에서 빼고 콜백리스트가 실행된다. 이때 Event.processed가 True로 활성화된다.
  • value = yield event라는 구문을 통해 event를 실행하고 반환되는 value를 구할 수 있다.
import simpy
def my_callback(event):
    print('Called back t = %d from %s' %(env.now, event)) 

def my_func(event):
    yield env.timeout(2)
    yield event.succeed()

env = simpy.Environment()
event = env.event() 
event.callbacks.append(my_callback)
env.process(my_func(event))
env.run(until=10)
Called back t = 2 from <Event() object at 0x1da97e992e0>
  • process()는 기본적으로 _resume() callback을 포함하고 있다. _resume()은 다음 라인을 실행하라는 콜백이다.
  • 콜백 리스트에 추가할 my_callback이라는 함수를 만들었다.
  • my_func()함수에서는 2라는 시간 뒤에 timeout event가 발생하고, event.succeed()로 Event를 트리거할 수 있다.
  • my_func라는 event가 발생하고 나서 2초 뒤에 callback 리스트에 저장된 함수인 my_callback이 실행되어 print문이 출력되었다.

 

 

    '전자공학/무선데이터통신' 카테고리의 다른 글
    • Packet Scheduling
    • Multiple Radio Access
    • Probability, Statistics, and Traffic Theory
    • Network Fundamentals
    wn42
    wn42
    코딩이랑 이것저것 하는 블로그

    티스토리툴바