forked from adrielklein/MM1-QUEUE
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMD1KQueue.py
205 lines (180 loc) · 9.37 KB
/
MD1KQueue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
'''
Created on Feb 16, 2013
@author: adrielklein
This module defines an MM1 Queuing System Simulator. Three class are defined in this module: Request, Controller, and Monitor.
The Request object represents a request which enters the system (gets born), waits in a queue, gets served, and leaves (dies).
Each request object has a record of its birth time, time at which it was serviced, and time at which it was born.
The Controller object is what manages the requests. the controller puts new requests into the queue and services each request
once it is at the front of the queue.
The Monitor object keeps records statistics about the queuing system at various points in time. The monitor gets information
about the system when the controller sends it a snapshot of the system.
So there is one instance of a Controller, one instance of a Monitor, and many instances of a request. The controller continually
creates new requests according to an exponential distributed arrival rate and continually serves the requests according to an exponentially
distributed service rate.
This is a single server queue so there can be at most one request being served at a given time. Incoming requests are served on a first-come
first-serve basis.
'''
from __future__ import division #Required for floating point divison.
from math import sqrt # Required to find variance
from heapq import heappush, heappop
from NumberGenerator import exponentialValue # Required to generate exponentially distributed random numbers
class Controller:
def __init__(self, arrivalRate, averageServiceTime, simulationTime):
self.arrivalRate = arrivalRate
self.serviceRate = 1/averageServiceTime
self.simulationTime = simulationTime
self.time = 0
self.queue = [] # A queue of events waiting to be served
self.beingServed = None # The request being served. None if no request is being served.
self.recentlyDied = None
self.monitor = Monitor() # Collects information about the state of the queue.
# Schedule is a heap with times as keys and events as values.
# The events will be representing by the following strings:
# "Birth", "Death", and "Monitor"
self.schedule = []
def runSimulation(self, monitorStartingTime):
self.monitorStartingTime = monitorStartingTime
#Add first Birth event to schedule
heappush(self.schedule, (exponentialValue(self.arrivalRate), "Birth"))
#Add first Monitor event to schedule.
heappush(self.schedule, (monitorStartingTime, "Monitor"))
while self.time < self.simulationTime:
'''
print str(self.time) + ": " + str(self.schedule)
print "Items in queue: " + str(len(self.queue))
print "Item in server: " + str(self.beingServed)
if self.sampleRequest != None:
print "Sample Waiting Time: " + str(self.recentlyDied.getWaitingTime())
print "Sample Queuing Time: " + str(self.sampleRequest.getQueueingTime())
'''
#Get the next event from the schedule
pair = heappop(self.schedule)
self.time = pair[0]
event = pair[1]
self.executeEvent(event)
def executeEvent(self,event):
if event =="Birth":
if self.time > self.monitorStartingTime:
self.monitor.incrementAttemptedRequests()
if len(self.queue) == 4:
if self.time > self.monitorStartingTime:
self.monitor.incrementRejectedRequests()
#Schedule next birth and return
timeOfNextBirth = self.time + exponentialValue(self.arrivalRate)
heappush(self.schedule, (timeOfNextBirth, "Birth"))
return
#Create new request and enqueue
newRequest = Request(self.time)
self.queue.append(newRequest)
#Schedule next birth
timeOfNextBirth = self.time + exponentialValue(self.arrivalRate)
heappush(self.schedule, (timeOfNextBirth, "Birth"))
# If queue only has one request and no requests are being served, then
# dequeue the request, start serving request, and schedule death
if len(self.queue) == 1 and self.beingServed == None:
request = self.queue.pop(0)
request.setServiceTime(self.time)
self.beingServed = request
#Schedule a death
deathTime = self.time + 0.015
heappush(self.schedule, (deathTime, "Death"))
elif event == "Death":
self.recentlyDied = self.beingServed
self.recentlyDied.setDeathTime(self.time)
'''
if self.time > self.monitorStartingTime:
self.monitor.recordDeadRequest(self.recentlyDied)
'''
self.beingServed = None
# Now there are no requests being served. If queue is empty, do nothing. Otherwise serve next request.
if len(self.queue) != 0:
request = self.queue.pop(0)
request.setServiceTime(self.time)
self.beingServed = request
#Schedule a death
deathTime = self.time + 0.015
heappush(self.schedule, (deathTime, "Death"))
else:
#This must be a monitor event
requestsWaiting = len(self.queue)
requestsInSystem = requestsWaiting
if self.beingServed != None:
requestsInSystem += 1
self.monitor.recordSnapshot(requestsWaiting, requestsInSystem, self.recentlyDied)
#Schedule next monitor event.
nextMonitorTime = self.time + exponentialValue(self.arrivalRate/2)
heappush(self.schedule, (nextMonitorTime, "Monitor"))
class Request:
def __init__(self, birthTime):
self.birthTime = birthTime
def setServiceTime(self, serviceTime):
self.serviceTime = serviceTime
def setDeathTime(self, deathTime):
self.deathTime = deathTime
def getWaitingTime(self):
return self.serviceTime - self.birthTime
def getQueuingTime(self):
return self.deathTime - self.birthTime
class Monitor:
def __init__(self):
self.numSnapshots = 0
self.numRequests = 0
self.attemptedRequests = 0
self.rejectedRequests = 0
self.requestsWaiting = []
self.requestsInSystem = []
self.waitingTimes = []
self.queuingTimes = []
def recordSnapshot(self, requestsWaiting, requestsInSystem, recentlyDied):
self.numSnapshots += 1
self.queuingTimes.append(recentlyDied.getQueuingTime())
self.requestsWaiting.append(requestsWaiting)
self.requestsInSystem.append(requestsInSystem)
def getMeanOfRequestsInSystem(self):
return sum(self.requestsInSystem)/self.numSnapshots
def getStandardDeviationOfRequestsInSystem(self):
mean = self.getMeanOfRequestsInSystem()
squaredDifferences = [(x - mean)**2 for x in self.requestsInSystem]
variance = sum(squaredDifferences) / self.numSnapshots
standardDeviation = sqrt(variance)
return standardDeviation
def incrementAttemptedRequests(self):
self.attemptedRequests += 1
def incrementRejectedRequests(self):
self.rejectedRequests += 1
def getRejectionProbability(self):
return self.rejectedRequests/ self.attemptedRequests
def getStandardDeviationOfRequestResult(self):
mean = self.getRejectionProbability()
variance = (self.rejectedRequests*(0 - mean)**2 + (self.attemptedRequests - self.rejectedRequests)*(1 - mean)**2)/self.attemptedRequests
standardDeviation = sqrt(variance)
return standardDeviation
def getStandardDeviationOfQueuingTime(self):
mean = sum(self.queuingTimes)/self.numSnapshots
squaredDifferences = [(x - mean)**2 for x in self.queuingTimes]
variance = sum(squaredDifferences)/ len(self.queuingTimes)
standardDeviation = sqrt(variance)
return standardDeviation
def recordDeadRequest(self, request):
self.numRequests += 1
self.waitingTimes.append(request.getWaitingTime())
self.queuingTimes.append(request.getQueuingTime())
def printReport(self):
print "Number of Snapshots Taken: " + str(self.numSnapshots)
print "Average Requests In System: " + str(sum(self.requestsInSystem)/self.numSnapshots)
print "Standard Deviation of Requests In System " + str(self.getStandardDeviationOfRequestsInSystem())
print
print "Number of Dead Requests: " + str(self.numSnapshots)
print "Average Queuing Time: " + str(sum(self.queuingTimes)/self.numSnapshots)
print "Standard Deviation of Queuing Time: " + str(self.getStandardDeviationOfQueuingTime())
print
print "Number of Requests Who Tried to Enter " + str(self.attemptedRequests)
print "Standard Deviation of Requests who were successful: " + str(self.getStandardDeviationOfRequestResult())
print "Rejection Probability: " + str(self.getRejectionProbability())
print
myController = Controller(60, 0.45, 200)
# Begin the simulation and start monitoring system at time 100.
myController.runSimulation(100)
#Print the results of the simulation
myController.monitor.printReport()
print