使用 Simpy 模拟 set-up/switch-on 次资源系统
Simulating a system of resource with set-up/switch-on times using Simpy
我想创建一个带有服务器的系统,在准备好服务之前需要时间进行设置。由于服务器数量随时间变化,我认为 Container 资源可能有用。每当有顾客排队时,就会建立一个服务器,先到的顾客会抢占先开的服务器,如下图。
- 客户 1 到达并请求服务器。
- 服务器 1 在 t1 秒内设置完毕。
- 客户 2 到达并请求服务器。
- 服务器 2 在 t2 秒内设置完毕。
- 服务器 2 已开启。
- 客户1占用服务器2
我想知道如何才能使上述过程真正起作用。如果我按如下安排事件,每个循环似乎都在 yield req
之后卡住了(yield req
不会将此请求放入队列并在稍后打开就绪服务器后立即完成请求在 yield servers.server.put(1)
?)
with servers.computer.get(1) as req:
yield req
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
"""0.09948 Job0 arrives
0.25648 Job1 arrives
0.37188 Job2 arrives
0.47028 Job3 arrives
0.53916 Job4 arrives
0.66893 Job5 arrives
"""
如果我把顺序改成下面这样,那么无论服务器ON的顺序如何,Customer-i肯定会占用Server-i
with servers.computer.get(1) as req:
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
yield req
这是完整的代码。
import simpy
LAM = 8 #arival rate of jobs
MU = 2 #service rate
ALPHA = 12 #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10
def generate_interarrival():
return np.random.exponential(1/LAM)
def generate_service():
return np.random.exponential(1/MU)
def switch_on():
return np.random.exponential(1/ALPHA)
class Generate_Job():
def arriving_job(env, servers):
global num_current_jobs, num_server_on, leaving_time_list
for i in range(MAX_NUM_JOB):
job = Job(name="Job%01d" % (i))
yield env.timeout(generate_interarrival())
print('{0:.5f}'.format(env.now), job.name, "arrives")
env.process(job.handling(env,servers))
class Room: # A room containing servers (resource)
def __init__(self, env):
self.computer = simpy.Container(env, capacity = 10000, init = 0)
class Job(object):
def __init__(self,name):
self.name = name
def handling(self, env, servers):
with servers.computer.get(1) as req:
yield req
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
print('{0:.5f}'.format(env.now), self.name, "occupies a server--")
yield env.timeout(generate_service()) #service time
print('{0:.5f}'.format(env.now), self.name, "leaves")
np.random.seed(0)
env = simpy.Environment()
servers = Room(env)
env.process(Generate_Job.arriving_job(env, servers))
env.run(until = UNTIL)
需要改变两件事
您的资源池一开始是空的,这意味着您对资源的第一次请求将永远等待,永远不会到达添加资源的代码。
我在请求之前添加了一个检查,如果需要将添加资源。由于添加资源有延迟,资源请求在资源“启动”时仍会等待。不要用 yield 调用此检查,否则请求将等待添加新资源,并且如果由于另一个添加而添加了资源,或者如果另一个作业完成并且 return 在添加了新资源
此外,容器不会 return 在“with”语句末尾添加资源,只有资源会那样做。
这里是固定代码,说说你的想法
"""
Simulation of a dynamic server pool
Server pool starts empty and servers are added as needed, but there is a delay
simulating start up time before the server is available to fulfill a resource request
Programmer: Matt
Wrote original version
Programmer: Michael R. Gibbs
Added server check for dynamicaly adding servers
Fixed return of resorces to pool
"""
import simpy
import numpy as np
LAM = 8 #arival rate of jobs
MU = 2 #service rate
ALPHA = 12 #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10
def generate_interarrival():
return np.random.exponential(1/LAM)
def generate_service():
return np.random.exponential(1/MU)
def switch_on():
return np.random.exponential(1/ALPHA)
def check_servers(env, servers):
"""
Checks the server pool to see if the pool has any avalable servers
if not then add a server, (there will be a delay before added server becomes available)
Call this without a yield so it does not block if a server is added
"""
print('{0:.5f}'.format(env.now), "checking server pool", "requests:",len(servers.get_queue), "servers:", servers.level)
if len(servers.get_queue) >= servers.level:
# will need another server
d = switch_on()
print('{0:.5f}'.format(env.now), "adding a server at " + '{0:.5f}'.format(env.now + d) + " --")
yield env.timeout(d) #switch on time
yield servers.put(1)
print('{0:.5f}'.format(env.now), "added a server--")
class Generate_Job():
def arriving_job(env, servers):
global num_current_jobs, num_server_on, leaving_time_list
for i in range(MAX_NUM_JOB):
job = Job(name="Job%01d" % (i))
yield env.timeout(generate_interarrival())
print('{0:.5f}'.format(env.now), job.name, "arrives")
env.process(job.handling(env,servers))
class Room: # A room containing servers (resource)
def __init__(self, env):
self.computer = simpy.Container(env, capacity = 10000, init = 0)
class Job(object):
def __init__(self,name):
self.name = name
def handling(self, env, servers):
# added a check to see if a resource pool needs another server.
env.process(check_servers(env,servers.computer))
print('{0:.5f}'.format(env.now), self.name, "requesting a server--")
with servers.computer.get(1) as req:
yield req
# if the queue is empty then the req is never filled and the next lines are never called
# need to do this before the rescource requests
#
# yield env.timeout(switch_on()) #switch on time
# yield servers.server.put(1)
print('{0:.5f}'.format(env.now), self.name, "occupies a server--")
yield env.timeout(generate_service()) #service time
print('{0:.5f}'.format(env.now), self.name, "leaves")
# containers do not return a resouce at the end of a "with"
# added a put
yield servers.computer.put(1)
np.random.seed(0)
env = simpy.Environment()
servers = Room(env)
env.process(Generate_Job.arriving_job(env, servers))
env.run(until = UNTIL)
我想创建一个带有服务器的系统,在准备好服务之前需要时间进行设置。由于服务器数量随时间变化,我认为 Container 资源可能有用。每当有顾客排队时,就会建立一个服务器,先到的顾客会抢占先开的服务器,如下图。
- 客户 1 到达并请求服务器。
- 服务器 1 在 t1 秒内设置完毕。
- 客户 2 到达并请求服务器。
- 服务器 2 在 t2 秒内设置完毕。
- 服务器 2 已开启。
- 客户1占用服务器2
我想知道如何才能使上述过程真正起作用。如果我按如下安排事件,每个循环似乎都在 yield req
之后卡住了(yield req
不会将此请求放入队列并在稍后打开就绪服务器后立即完成请求在 yield servers.server.put(1)
?)
with servers.computer.get(1) as req:
yield req
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
"""0.09948 Job0 arrives
0.25648 Job1 arrives
0.37188 Job2 arrives
0.47028 Job3 arrives
0.53916 Job4 arrives
0.66893 Job5 arrives
"""
如果我把顺序改成下面这样,那么无论服务器ON的顺序如何,Customer-i肯定会占用Server-i
with servers.computer.get(1) as req:
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
yield req
这是完整的代码。
import simpy
LAM = 8 #arival rate of jobs
MU = 2 #service rate
ALPHA = 12 #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10
def generate_interarrival():
return np.random.exponential(1/LAM)
def generate_service():
return np.random.exponential(1/MU)
def switch_on():
return np.random.exponential(1/ALPHA)
class Generate_Job():
def arriving_job(env, servers):
global num_current_jobs, num_server_on, leaving_time_list
for i in range(MAX_NUM_JOB):
job = Job(name="Job%01d" % (i))
yield env.timeout(generate_interarrival())
print('{0:.5f}'.format(env.now), job.name, "arrives")
env.process(job.handling(env,servers))
class Room: # A room containing servers (resource)
def __init__(self, env):
self.computer = simpy.Container(env, capacity = 10000, init = 0)
class Job(object):
def __init__(self,name):
self.name = name
def handling(self, env, servers):
with servers.computer.get(1) as req:
yield req
yield env.timeout(switch_on()) #switch on time
yield servers.server.put(1)
print('{0:.5f}'.format(env.now), self.name, "occupies a server--")
yield env.timeout(generate_service()) #service time
print('{0:.5f}'.format(env.now), self.name, "leaves")
np.random.seed(0)
env = simpy.Environment()
servers = Room(env)
env.process(Generate_Job.arriving_job(env, servers))
env.run(until = UNTIL)
需要改变两件事
您的资源池一开始是空的,这意味着您对资源的第一次请求将永远等待,永远不会到达添加资源的代码。
我在请求之前添加了一个检查,如果需要将添加资源。由于添加资源有延迟,资源请求在资源“启动”时仍会等待。不要用 yield 调用此检查,否则请求将等待添加新资源,并且如果由于另一个添加而添加了资源,或者如果另一个作业完成并且 return 在添加了新资源
此外,容器不会 return 在“with”语句末尾添加资源,只有资源会那样做。
这里是固定代码,说说你的想法
"""
Simulation of a dynamic server pool
Server pool starts empty and servers are added as needed, but there is a delay
simulating start up time before the server is available to fulfill a resource request
Programmer: Matt
Wrote original version
Programmer: Michael R. Gibbs
Added server check for dynamicaly adding servers
Fixed return of resorces to pool
"""
import simpy
import numpy as np
LAM = 8 #arival rate of jobs
MU = 2 #service rate
ALPHA = 12 #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10
def generate_interarrival():
return np.random.exponential(1/LAM)
def generate_service():
return np.random.exponential(1/MU)
def switch_on():
return np.random.exponential(1/ALPHA)
def check_servers(env, servers):
"""
Checks the server pool to see if the pool has any avalable servers
if not then add a server, (there will be a delay before added server becomes available)
Call this without a yield so it does not block if a server is added
"""
print('{0:.5f}'.format(env.now), "checking server pool", "requests:",len(servers.get_queue), "servers:", servers.level)
if len(servers.get_queue) >= servers.level:
# will need another server
d = switch_on()
print('{0:.5f}'.format(env.now), "adding a server at " + '{0:.5f}'.format(env.now + d) + " --")
yield env.timeout(d) #switch on time
yield servers.put(1)
print('{0:.5f}'.format(env.now), "added a server--")
class Generate_Job():
def arriving_job(env, servers):
global num_current_jobs, num_server_on, leaving_time_list
for i in range(MAX_NUM_JOB):
job = Job(name="Job%01d" % (i))
yield env.timeout(generate_interarrival())
print('{0:.5f}'.format(env.now), job.name, "arrives")
env.process(job.handling(env,servers))
class Room: # A room containing servers (resource)
def __init__(self, env):
self.computer = simpy.Container(env, capacity = 10000, init = 0)
class Job(object):
def __init__(self,name):
self.name = name
def handling(self, env, servers):
# added a check to see if a resource pool needs another server.
env.process(check_servers(env,servers.computer))
print('{0:.5f}'.format(env.now), self.name, "requesting a server--")
with servers.computer.get(1) as req:
yield req
# if the queue is empty then the req is never filled and the next lines are never called
# need to do this before the rescource requests
#
# yield env.timeout(switch_on()) #switch on time
# yield servers.server.put(1)
print('{0:.5f}'.format(env.now), self.name, "occupies a server--")
yield env.timeout(generate_service()) #service time
print('{0:.5f}'.format(env.now), self.name, "leaves")
# containers do not return a resouce at the end of a "with"
# added a put
yield servers.computer.put(1)
np.random.seed(0)
env = simpy.Environment()
servers = Room(env)
env.process(Generate_Job.arriving_job(env, servers))
env.run(until = UNTIL)