-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimulate.py
More file actions
414 lines (319 loc) · 13.4 KB
/
simulate.py
File metadata and controls
414 lines (319 loc) · 13.4 KB
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import random
import pickle
import math
import numpy as np
s = sys.argv[1]
# obtain random settings and save it to dump file
# rand_state = random.getstate()
# pickle.dump(rand_state, open("rand/rand_state_sim.p", "wb"))
# load the saved random settings and apply it
# rand_state = pickle.load(open("rand/rand_state_sim.p", "rb"))
# random.setstate(rand_state)
# either trace / random
mode = np.loadtxt(f"config/mode_{s}.txt", dtype="str")
# trace: n, n_0, T_limit
# random: n, n_0, T_limit, time_end (random)
para = np.loadtxt(f"config/para_{s}.txt")
# random: lamb, a_2l, a_2u
interarrival = np.loadtxt(f"config/interarrival_{s}.txt")
if mode == "trace":
service = np.loadtxt(f"config/service_{s}.txt")
# random: p_0; alpha_0, beta_0, eta_0; alpha_1, eta_1
else:
with open(f"config/service_{s}.txt", "r") as f:
lines = f.readlines()
service = [line.split() for line in lines]
n = int(para[0])
n_0 = int(para[1])
n_1 = n - n_0
T_limit = para[2]
time_end = -1
lamb = -1
a_2l = -1
a_2u = -1
p_0 = -1
alpha_0 = -1
beta_0 = -1
eta_0 = -1
gamma_0 = -1
alpha_1 = -1
eta_1 = -1
gamma_1 = -1
if mode == "random":
time_end = para[3]
lamb = interarrival[0]
a_2l = interarrival[1]
a_2u = interarrival[2]
p_0 = float(service[0][0])
alpha_0 = float(service[1][0])
beta_0 = float(service[1][1])
eta_0 = float(service[1][2])
gamma_0 = math.pow(alpha_0, -eta_0) - math.pow(beta_0, -eta_0)
alpha_1 = float(service[2][0])
eta_1 = float(service[2][1])
gamma_1 = math.pow(alpha_1, -eta_1)
print()
print("mode", mode)
# print("para", para)
# print("interarrival", interarrival)
# print("service", service)
print()
print("n", n)
print("n_0", n_0)
print("n_1", n_1)
print("T_limit", T_limit)
print("time_end", time_end)
print()
print("lamb", lamb)
print("a_2l", a_2l)
print("a_2u", a_2u)
print()
print("p_0", p_0)
print("alpha_0", alpha_0)
print("beta_0", beta_0)
print("eta_0", eta_0)
print("gamma_0", gamma_0)
print("alpha_1", alpha_1)
print("eta_1", eta_1)
print("gamma_1", gamma_1)
i = 0
crt_0 = 0
crt_1 = 0
njobs_0 = 0
njobs_1 = 0
# Intialise the master clock
master_clock = 0
# Intialise server status
server_status = np.full((n,), False, dtype=bool)
# Initialising the arrival event
next_arrival_time = 0
next_arrival_s_time = 0
next_arrival_class = "1"
# Initialise the departure event to empty
server_job_d_time = np.empty((n,))
server_job_d_time[:] = np.inf # inf to denote an empty event
server_job_a_time = np.zeros((n,))
server_job_s_time = np.zeros((n,))
server_job_class = np.full((n,), "1", dtype=str)
arrived_job = []
completed_job = [] # arrival time, departure time, classification
# Initialise buffer
# The inner lists are lists with 2 elements.
# The elements contain the arrival time and service time of a job.
queue_0 = [] # arrival time, service time, class
queue_1 = [] # arrival time, service time, class, real arrival time
def get_next_arrival():
global next_arrival_time
global next_arrival_s_time
global next_arrival_class
if mode == "trace":
next_arrival_time = master_clock + interarrival[i]
next_arrival_s_time = service[i][0]
next_arrival_class = str(int(service[i][1]))
else:
next_arrival_time = master_clock + random.uniform(a_2l, a_2u) * random.expovariate(lamb)
# GROUP 0
if random.random() < p_0: # 0.0 <= X < 1.0
next_arrival_class = "0"
next_arrival_s_time = math.exp(math.log(math.pow(alpha_0, -eta_0) - gamma_0 * random.random()) / -eta_0)
# GROUP 1
else:
next_arrival_class = "1"
next_arrival_s_time = math.exp(math.log(math.pow(alpha_1, -eta_1) - gamma_1 * random.random()) / -eta_1)
get_next_arrival()
# repeat iteration until terminating condition
while True:
# Find the next departing job from server job list
next_departure_time = np.min(server_job_d_time)
# Find out whether the next event is an arrival or departure
# unspecified if an arrival event and a departure event occur at the same time
if next_arrival_time < next_departure_time:
next_event_time = next_arrival_time
next_event_type = 'arrival'
else:
next_event_time = next_departure_time
next_event_type = 'departure'
# update master clock
master_clock = next_event_time
# REPEAT UNTIL (breaking condition for random mode)
# master clock exceeds the provided time limit
if mode == "random":
if master_clock > time_end:
break
# print(master_clock, next_event_type)
# take actions depending on the event type
# ===============================================================
# if it is an arrival event
if next_event_type == 'arrival':
arrived_job.append([next_arrival_time, next_arrival_s_time, next_arrival_class])
# GROUP 0
# for arrival of class "0" jobs
if next_arrival_class == "0":
# if all servers are busy, add job to buffer
if np.all(server_status[:n_0]):
# check if within T_limit
if next_arrival_s_time <= T_limit:
queue_0.append([next_arrival_time, next_arrival_s_time, "0"])
else:
queue_0.append([next_arrival_time, next_arrival_s_time, "2"])
# if there are idle servers, bypass buffer and serve immediately
else:
# Send job to the closest available server
idle_server = np.min(np.where(server_status[:n_0]==False))
# Schedule departure event
if next_arrival_s_time <= T_limit:
server_job_d_time[idle_server] = next_arrival_time + next_arrival_s_time
server_job_class[idle_server] = "0"
else:
server_job_d_time[idle_server] = next_arrival_time + T_limit
server_job_class[idle_server] = "2"
server_job_a_time[idle_server] = next_arrival_time
server_job_s_time[idle_server] = next_arrival_s_time
# Mark server as Busy
server_status[idle_server] = True
# GROUP 1
# for arrival of class "1" jobs
else:
# if all servers are busy, add job to buffer
if np.all(server_status[n_0:]):
queue_1.append([next_arrival_time, next_arrival_s_time, "1"])
# if there are idle servers, bypass buffer and serve immediately
else:
# Send job to the closest available server
idle_server = np.min(np.where(server_status[n_0:]==False)) + n_0
# Schedule departure event
server_job_d_time[idle_server] = next_arrival_time + next_arrival_s_time
server_job_class[idle_server] = "1"
server_job_a_time[idle_server] = next_arrival_time
server_job_s_time[idle_server] = next_arrival_s_time
# Mark server as Busy
server_status[idle_server] = True
if mode == "trace":
# increment counter for next arrival
i += 1
# if there is no more next arrival
if i >= len(interarrival):
next_arrival_time = np.inf
# print(next_arrival_time)
# print(server_status)
# print("queue_0", queue_0)
# print("queue_1", queue_1)
# print()
continue
# Get to next new job and schedule its arrival
get_next_arrival()
# next_arrival_time = master_clock + interarrival[i]
# next_arrival_s_time = service[i][0]
# next_arrival_class = str(int(service[i][1]))
# ===============================================================
# if it is a departure event
else:
next_departure_server = np.argmin(server_job_d_time) # which server
next_departure_a_time = server_job_a_time[next_departure_server]
next_departure_s_time = server_job_s_time[next_departure_server]
next_departure_class = server_job_class[next_departure_server]
# add to permanently completed job list
if next_departure_class in ["0", "1", "3"]:
completed_job.append([next_departure_a_time, master_clock, next_departure_class])
# GROUP 0
# 0: jobs that are correctly classified
# 2: jobs that await to be sent to GROUP 1
if next_departure_class in ["0", "2"]:
if next_departure_class == "0":
crt_0 = crt_0 + (master_clock - next_departure_a_time)
njobs_0 = njobs_0 + 1
# if buffer is not empty, take a job from buffer to replace the departed server slot
if len(queue_0):
# Take the first job in buffer
first_job_in_queue = queue_0.pop(0)
# Schedule the next departure
if first_job_in_queue[2] == "0": #first_job_in_queue[1] <= T_limit:
server_job_d_time[next_departure_server] = master_clock + first_job_in_queue[1]
server_job_class[next_departure_server] = "0"
else:
server_job_d_time[next_departure_server] = master_clock + T_limit
server_job_class[next_departure_server] = "2"
server_job_a_time[next_departure_server] = first_job_in_queue[0]
server_job_s_time[next_departure_server] = first_job_in_queue[1]
# if buffer is empty
else:
# Indicate that no next departure for that server
server_job_d_time[next_departure_server] = np.inf
# Mark server as Idle
server_status[next_departure_server] = False
# recirculate job immediately to GROUP 1
if next_departure_class == "2":
# if all servers are busy, add job to GROUP 1 buffer
if np.all(server_status[n_0:]):
queue_1.append([next_departure_a_time, next_departure_s_time, "3"])
# if there are idle servers, bypass buffer and serve immediately
else:
# Send the job to the closest available server
idle_server = np.min(np.where(server_status[n_0:]==False)) + n_0
# Schedule departure time as arrival time + service time
server_job_d_time[idle_server] = master_clock + next_departure_s_time
server_job_class[idle_server] = "3"
server_job_a_time[idle_server] = next_departure_a_time # the original arrival time
server_job_s_time[idle_server] = next_departure_s_time
# Mark server as Busy
server_status[idle_server] = True
# GROUP 1
# 1: jobs that are classified as GROUP 1
# 3: jobs that are recirculated from GROUP 0
elif next_departure_class in ["1", "3"]:
if next_departure_class == "1":
crt_1 = crt_1 + (master_clock - next_departure_a_time)
njobs_1 = njobs_1 + 1
# if buffer is not empty, take a job from buffer
if len(queue_1):
# Take the first job in buffer
first_job_in_queue = queue_1.pop(0)
# Schedule the next departure
server_job_d_time[next_departure_server] = master_clock + first_job_in_queue[1]
server_job_class[next_departure_server] = first_job_in_queue[2]
server_job_a_time[next_departure_server] = first_job_in_queue[0]
server_job_s_time[next_departure_server] = first_job_in_queue[1]
# if buffer is empty
else:
# Indicate that no next departure for that server
server_job_d_time[next_departure_server] = np.inf
# Mark server as Idle
server_status[next_departure_server] = False
# print(next_arrival_time)
# print(server_status)
# print("queue_0", queue_0)
# print("queue_1", queue_1)
# print()
# REPEAT UNTIL (breaking condition for trace mode)
# finished reading all tracing and completed all departures
if mode == "trace":
if i >= len(interarrival) and np.min(server_job_d_time) == np.inf:
break # next_arrival_time == np.inf
# the estimated mean response time discarding recirculated jobs
mrt_0 = round(crt_0 / njobs_0, 4)
mrt_1 = round(crt_1 / njobs_1, 4)
print()
print("crt_0", crt_0)
print("crt_1", crt_1)
print("njobs_0", njobs_0)
print("njobs_1", njobs_1)
print("mrt_0", mrt_0)
print("mrt_1", mrt_1)
print()
completed_job.sort(key=lambda f: f[1]) # sort in ascending order to departure time
# for y in completed_job:
# print(y)
for y in range(len(completed_job)):
for x in range(2):
completed_job[y][x] = f"{'%.4f' % round(completed_job[y][x], 4)}"
if completed_job[y][2] == "3":
completed_job[y][2] = "r0" # rectify class label
for y in range(len(completed_job)):
completed_job[y] = " ".join(completed_job[y]) + "\n"
with open(f"output/mrt_{s}.txt", "w") as f:
f.writelines([f"{'%.4f' % mrt_0} {'%.4f' % mrt_1}"])
with open(f"output/dep_{s}.txt", "w") as f:
f.writelines(completed_job)