Skip to content

Instantly share code, notes, and snippets.

@dehowell
Created May 15, 2016 18:24
Show Gist options
  • Select an option

  • Save dehowell/0eb6c5df5925f7eafa8461a6ff1b1273 to your computer and use it in GitHub Desktop.

Select an option

Save dehowell/0eb6c5df5925f7eafa8461a6ff1b1273 to your computer and use it in GitHub Desktop.
Example of modeling a blocking-IO web server with simpy
#!/usr/bin/env python
import contextlib
import itertools
import random
import pandas
import simpy
QUERY_DURATION = 0.4
RENDER_DURATION = 0.4
class Tracer(object):
'''Service for tracking start/end time of events.'''
def __init__(self, env):
self.env = env
self.statistics = {}
def start(self, request_id, event_name):
'''Record the start of an event in the simulation.'''
tracer_key = request_id, event_name
if tracer_key in self.statistics:
print self.to_dataframe()
raise ValueError('(%s, %s) already started' % tracer_key)
self.statistics[tracer_key] = {
'request_id': request_id,
'event_name': event_name,
'start_time': self.env.now
}
def end(self, request_id, event_name):
'''Record the end of an event in the simulation.'''
tracer_key = request_id, event_name
if tracer_key not in self.statistics:
raise ValueError('(%s, %s) not found' % tracer_key)
record = self.statistics[tracer_key]
record['duration'] = self.env.now - record['start_time']
@contextlib.contextmanager
def __call__(self, request_id, event_name):
self.start(request_id, event_name)
yield
self.end(request_id, event_name)
def to_dataframe(self):
return pandas.DataFrame(self.statistics.values())
def request_generator(env, tracer, socket_pool, database_pool, interval=0.1):
for n in itertools.count():
name = 'request-%02d' % n
req = request(env, name, tracer, socket_pool, database_pool)
env.process(req)
t = random.expovariate(1.0 / interval)
yield env.timeout(1)
class ServiceNode(object):
def __init__(self, env, tracer, capacity, resource_name):
self.env = env
self.tracer = tracer
self.resource = simpy.Resource(env, capacity = capacity)
self.resource_name = resource_name
def __call__(self, unit_id, task_name, task_duration):
with self.resource.request() as req:
with self.tracer(unit_id, '%s_wait' % self.resource_name):
yield req
with self.tracer(unit_id, task_name):
yield self.env.timeout(random.expovariate(1.0 / task_duration))
def request(env, name, tracer, socket_pool, database):
with tracer(name, 'request'), socket_pool.request() as req:
# Wait for an available socket listener in the web server.
with tracer(name, 'socket_wait'):
yield req
# Get the data for the model
yield env.process(database(name, 'get_model', QUERY_DURATION))
# with database_pool.request() as req:
# with tracer(name, 'database_wait'):
# yield req
#
# with tracer(name, 'database_query'):
# yield env.timeout(QUERY_DURATION)
with tracer(name, 'render'):
yield env.timeout(RENDER_DURATION)
def run():
random.seed(1)
env = simpy.Environment()
tracer = Tracer(env)
listener = simpy.Resource(env, capacity = 10)
database = ServiceNode(env, tracer, 1, 'database')
requests = request_generator(env, tracer, listener, database, interval = 0.001)
env.process(requests)
env.run(until=1000)
return tracer.to_dataframe()
if __name__ == '__main__':
import sys
results = run()
results.to_csv(sys.stdout, index=False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment