'Simpy: How to monitor resource use
I am trying to understand how to collect data for computing resource use, for example, the average number of customers waiting in line. I looked at the documentation at the following link, but it is just too much for me. I am looking for an example of how it is used and how to compute time-based average line length. I appreciate any guidance.
https://simpy.readthedocs.io/en/latest/topical_guides/monitoring.html#monitoring-your-processes
Solution 1:[1]
Resources do not have utilization logging. You will need to collect that yourself. Monkey patching is a way to wrap resource requests with code to collects the stats without changing how resource requests are called. A more simple way is to just make a logger, and add a log call where you need it. That is how I did it in my example. The down side is you have to remember to add the logging code were you need it.
simple resources have the following properties for collecting stats: capacity, count (number of users with a resource, users (list of users with a resource), queue (list of pending resource requests)
"""
A quick example on how to get average line length
using a custom logger class
programmer: Michael R. Gibbs
"""
import simpy
import numpy as np
import pandas as pd
class LineLogger():
"""
logs the size of a resource line
"""
def __init__(self, env):
self.env = env
# the log
self.samples_df = pd.DataFrame(columns=['time','len'])
def log(self, time, len):
"""
log a time and length of the resoure request queue
time: time the messure is taken
len: length of the queue
"""
self.samples_df = self.samples_df.append({'time':time,'len':len},ignore_index=True)
def get_ave_line(self):
"""
finds the time weighted average of the queue length
"""
# use the next row to figure out how long the queue was at that length
self.samples_df['time_span'] = self.samples_df['time'].shift(-1) - self.samples_df['time']
# drop the last row because it would have a infinate time span
trimed_samples_df = self.samples_df[0:-1]
ave = np.average(trimed_samples_df['len'], weights=trimed_samples_df['time_span'])
return ave
def task(env, res, line_logger):
"""
A simple task that grabs a resouce for a bit of time
"""
with res.request() as req: # Generate a request event
# requester enters queue for resouce
line_logger.log(env.now,len(res.queue))
yield req
# requester got a resource and leaves requeuest queue
# if the resource was available when the request was made, then time in queue will be 0
line_logger.log(env.now,len(res.queue))
# keep resource to build a queue
yield env.timeout(3.5)
def gen_tasks(env, res, line_logger):
"""
generates 5 tasks to seize a resource
building a queue over time
"""
for i in range(5):
env.process(task(env,res,line_logger))
# put some time between requests
yield env.timeout(1)
if __name__ == '__main__':
env = simpy.Environment()
res = simpy.Resource(env, capacity=1)
line_logger = LineLogger(env)
env.process(gen_tasks(env,res,line_logger))
env.run(100)
print("finish sim")
print("average queue length is: ",line_logger.get_ave_line())
print()
print("log data")
print(line_logger.samples_df)
print()
print("done")
Solution 2:[2]
Use a process running in parallel with your main process to monitor utilization. Here is boilerplate code for a generator function you can use in the monitoring process.
data = []
def monitor_process(env, resource):
"""
Generator for monitoring process
that shares the environment with the main process
and collects information.
"""
while True:
item = (env.now,
resource.count,
len(resource.queue))
data.append(item)
yield env.timeout(0.25)
This generator function is set up to poll the resource object 4 times each simulation step and puts the result in an array. You can change the polling frequency. Call this generator like so:
env.process(monitor_process(env, target_resource))
When you call env.run(until=120)
(for example) to run your main process, this process will run parallel and log resource statistics.
I have implemented monkey-patching for comparison to this approach. Monkey-patching decorates some of a resource's methods with logging features. The code is more elegant but also more complex. Moreover, with monkey-patching, the resource stats will be logged each time an event occurs, i.e. any of the target resource's get, put, request or release methods is called. The approach I have shown here will log resource stats at regular time intervals and the code is relatively simpler.
Hope this helps.
Cheers!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Michael |
Solution 2 | Sun Bee |