Skip to content

Commit 35a9f43

Browse files
Merge pull request #131 from CiwPython/performance
Performance improvements
2 parents cae720f + 80c5a3b commit 35a9f43

12 files changed

Lines changed: 1909 additions & 1031 deletions

File tree

ciw/arrival_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def decide_baulk(self, next_node, next_individual):
4141
Either makes an individual baulk, or sends the individual
4242
to the next node
4343
"""
44-
if next_node.baulking_functions[self.next_class] == None:
44+
if next_node.baulking_functions[self.next_class] is None:
4545
self.send_individual(next_node, next_individual)
4646
else:
4747
rnd_num = random()
@@ -55,7 +55,7 @@ def find_next_event_date(self):
5555
"""
5656
Finds the time of the next arrival.
5757
"""
58-
times = [[self.event_dates_dict[nd+1][clss]
58+
times = [[self.event_dates_dict[nd + 1][clss]
5959
for clss in range(len(self.event_dates_dict[1]))]
6060
for nd in range(len(self.event_dates_dict))]
6161

ciw/data_record.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,6 @@
1-
from __future__ import division
1+
from collections import namedtuple
22

3-
class DataRecord(object):
4-
"""
5-
A class for a data record
6-
"""
7-
def __init__(self,
8-
arrival_date,
9-
service_end_date,
10-
service_start_date,
11-
exit_date,
12-
node,
13-
destination,
14-
customer_class,
15-
queue_size_at_arrival,
16-
queue_size_at_departure):
17-
"""
18-
Initialises a data record instance.
19-
"""
20-
self.arrival_date = arrival_date
21-
self.service_time = service_end_date - service_start_date
22-
self.service_start_date = service_start_date
23-
self.exit_date = exit_date
24-
self.customer_class = customer_class
25-
self.queue_size_at_arrival = queue_size_at_arrival
26-
self.queue_size_at_departure = queue_size_at_departure
27-
self.service_end_date = service_end_date
28-
self.wait = self.service_start_date - self.arrival_date
29-
self.blocked = self.exit_date - self.service_end_date
30-
self.node = node
31-
self.destination = destination
32-
33-
def __repr__(self):
34-
"""
35-
Represents the Data Record
36-
"""
37-
return "Data Record"
3+
DataRecord = namedtuple('Record', ['id_number', 'customer_class', 'node',
4+
'arrival_date', 'waiting_time', 'service_start_date', 'service_time',
5+
'service_end_date', 'time_blocked', 'exit_date', 'destination',
6+
'queue_size_at_arrival', 'queue_size_at_departure'])

ciw/exit_node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ def __init__(self):
99
Initialise a node.
1010
"""
1111
self.all_individuals = []
12+
self.number_of_individuals = 0
1213
self.id_number = -1
1314
self.next_event_date = float("Inf")
1415
self.node_capacity = float("Inf")
15-
self.number_completed = 0
1616

1717
def __repr__(self):
1818
"""
@@ -25,7 +25,7 @@ def accept(self, next_individual, current_time):
2525
Adds customer to the list of completed customers
2626
"""
2727
self.all_individuals.append(next_individual)
28-
self.number_completed += 1
28+
self.number_of_individuals += 1
2929

3030
def update_next_event_date(self):
3131
"""

ciw/node.py

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from random import random
33
import os
44
from csv import writer
5+
from math import isinf
56

67
import networkx as nx
78

@@ -42,6 +43,7 @@ def __init__(self, id_, simulation):
4243
self.class_change = node.class_change_matrix
4344
self.individuals = [[] for _ in
4445
range(simulation.number_of_priority_classes)]
46+
self.number_of_individuals = 0
4547
self.id_number = id_
4648
self.baulking_functions = [self.simulation.network.customer_classes[
4749
clss].baulking_functions[id_-1] for clss in range(
@@ -52,7 +54,7 @@ def __init__(self, id_, simulation):
5254
else:
5355
self.next_event_date = float("Inf")
5456
self.blocked_queue = []
55-
if self.c < float('Inf'):
57+
if not isinf(self.c):
5658
self.servers = self.create_starting_servers()
5759
self.highest_id = self.c
5860
self.simulation.deadlock_detector.initialise_at_node(self)
@@ -66,11 +68,6 @@ def all_individuals(self):
6668
return [i for priority_class in self.individuals
6769
for i in priority_class]
6870

69-
@property
70-
def number_of_individuals(self):
71-
return len(self.all_individuals)
72-
73-
7471
def __repr__(self):
7572
"""
7673
Representation of a node.
@@ -87,6 +84,7 @@ def accept(self, next_individual, current_time):
8784
next_individual, current_time)
8885
next_individual.queue_size_at_arrival = self.number_of_individuals
8986
self.individuals[next_individual.priority_class].append(next_individual)
87+
self.number_of_individuals += 1
9088
self.simulation.statetracker.change_state_accept(
9189
self.id_number, next_individual.customer_class)
9290

@@ -106,6 +104,7 @@ def attach_server(self, server, individual):
106104
server.cust = individual
107105
server.busy = True
108106
individual.server = server
107+
server.next_end_service_date = individual.service_end_date
109108
self.simulation.deadlock_detector.action_at_attach_server(
110109
self, server, individual)
111110

@@ -120,24 +119,24 @@ def begin_service_if_possible_accept(self,
120119
next_individual.service_time = self.get_service_time(
121120
next_individual.customer_class, current_time)
122121
if self.free_server():
123-
if self.c < float('Inf'):
124-
self.attach_server(self.find_free_server(),
125-
next_individual)
126122
next_individual.service_start_date = self.get_now(current_time)
127123
next_individual.service_end_date = self.increment_time(
128124
current_time, next_individual.service_time)
125+
if not isinf(self.c):
126+
self.attach_server(self.find_free_server(),
127+
next_individual)
129128

130129
def begin_interrupted_individuals_service(self, current_time, srvr):
131130
"""
132131
Restarts the next interrupted individual's service (by
133-
resampking service time)
132+
resampling service time)
134133
"""
135134
ind = [i for i in self.interrupted_individuals][0]
136-
self.attach_server(srvr, ind)
137135
ind.service_time = self.get_service_time(ind.customer_class,
138136
current_time)
139137
ind.service_end_date = self.increment_time(self.get_now(current_time),
140138
ind.service_time)
139+
self.attach_server(srvr, ind)
141140
self.interrupted_individuals.remove(ind)
142141

143142
def begin_service_if_possible_change_shift(self, current_time):
@@ -151,26 +150,26 @@ def begin_service_if_possible_change_shift(self, current_time):
151150
self.begin_interrupted_individuals_service(current_time, srvr)
152151
elif len([i for i in self.all_individuals if not i.server]) > 0:
153152
ind = [i for i in self.all_individuals if not i.server][0]
154-
self.attach_server(srvr, ind)
155153
ind.service_start_date = self.get_now(current_time)
156154
ind.service_end_date = self.increment_time(
157155
ind.service_start_date, ind.service_time)
156+
self.attach_server(srvr, ind)
158157

159158
def begin_service_if_possible_release(self, current_time):
160159
"""
161160
Begins the service of the next individual, giving
162161
that customer a service time, end date and node.
163162
"""
164-
if self.free_server() and self.c != float('Inf'):
163+
if self.free_server() and (not isinf(self.c)):
165164
srvr = self.find_free_server()
166165
if len(self.interrupted_individuals) > 0:
167166
self.begin_interrupted_individuals_service(current_time, srvr)
168167
elif len([i for i in self.all_individuals if not i.server]) > 0:
169168
ind = [i for i in self.all_individuals if not i.server][0]
170-
self.attach_server(srvr, ind)
171169
ind.service_start_date = self.get_now(current_time)
172170
ind.service_end_date = self.increment_time(
173171
ind.service_start_date, ind.service_time)
172+
self.attach_server(srvr, ind)
174173

175174
def block_individual(self, individual, next_node):
176175
"""
@@ -203,7 +202,7 @@ def change_shift(self):
203202
Add servers and deletes or indicates which servers
204203
should go off duty.
205204
"""
206-
shift = self.next_event_date%self.cyclelength
205+
shift = self.next_event_date % self.cyclelength
207206

208207
try: indx = self.schedule.index(shift)
209208
except:
@@ -254,7 +253,7 @@ def free_server(self):
254253
"""
255254
Returns True if a server is available, False otherwise
256255
"""
257-
if self.c == float('Inf'):
256+
if isinf(self.c):
258257
return True
259258
return len([svr for svr in self.servers if not svr.busy]) > 0
260259

@@ -270,9 +269,8 @@ def find_next_individual(self):
270269
"""
271270
Finds the next individual that should now finish service
272271
"""
273-
next_individual_indices = [i for i, x in enumerate(
274-
[ind.service_end_date for ind in self.all_individuals]
275-
) if x == self.next_event_date]
272+
next_individual_indices = [i for i, ind in enumerate(
273+
self.all_individuals) if ind.service_end_date == self.next_event_date]
276274
if len(next_individual_indices) > 1:
277275
next_individual_index = random_choice(next_individual_indices)
278276
else:
@@ -283,7 +281,7 @@ def find_server_utilisation(self):
283281
"""
284282
Finds the overall server utilisation for the node
285283
"""
286-
if self.c == float('Inf') or self.c == 0:
284+
if isinf(self.c) or self.c == 0:
287285
self.server_utilisation = None
288286
else:
289287
for server in self.servers:
@@ -299,7 +297,9 @@ def finish_service(self):
299297
self.change_customer_class(next_individual)
300298
next_node = self.next_node(next_individual.customer_class)
301299
next_individual.destination = next_node.id_number
302-
if len(next_node.all_individuals) < next_node.node_capacity:
300+
if not isinf(self.c):
301+
next_individual.server.next_end_service_date = float('Inf')
302+
if next_node.number_of_individuals < next_node.node_capacity:
303303
self.release(next_individual_index, next_node,
304304
self.next_event_date)
305305
else:
@@ -352,9 +352,10 @@ def release(self, next_individual_index, next_node, current_time):
352352
"""
353353
next_individual = self.all_individuals[next_individual_index]
354354
self.individuals[next_individual.prev_priority_class].remove(next_individual)
355-
next_individual.queue_size_at_departure = len(self.all_individuals)
355+
self.number_of_individuals -= 1
356+
next_individual.queue_size_at_departure = self.number_of_individuals
356357
next_individual.exit_date = current_time
357-
if self.c < float('Inf'):
358+
if not isinf(self.c):
358359
self.detatch_server(next_individual.server, next_individual)
359360
self.write_individual_record(next_individual)
360361
self.simulation.statetracker.change_state_release(self.id_number,
@@ -419,10 +420,14 @@ def update_next_event_date(self, current_time):
419420
"""
420421
Finds the time of the next event at this node
421422
"""
422-
next_end_service = min([ind.service_end_date
423-
for ind in self.all_individuals
424-
if not ind.is_blocked
425-
if ind.service_end_date >= current_time] + [float("Inf")])
423+
if not isinf(self.c):
424+
next_end_service = min([s.next_end_service_date
425+
for s in self.servers] + [float("Inf")])
426+
else:
427+
next_end_service = min([ind.service_end_date
428+
for ind in self.all_individuals
429+
if not ind.is_blocked
430+
if ind.service_end_date >= current_time] + [float("Inf")])
426431
if self.schedule:
427432
next_shift_change = self.next_shift_change
428433
self.next_event_date = min(
@@ -435,7 +440,7 @@ def wrap_up_servers(self, current_time):
435440
Updates the servers' total_time and busy_time
436441
as the end of the simulation run.
437442
"""
438-
if self.c != float('Inf'):
443+
if not isinf(self.c):
439444
for srvr in self.servers:
440445
srvr.total_time = self.increment_time(current_time, -srvr.start_date)
441446
if srvr.busy:
@@ -458,15 +463,19 @@ def write_individual_record(self, individual):
458463
- Queue size at arrival
459464
- Queue size at departure
460465
"""
461-
record = DataRecord(individual.arrival_date,
462-
individual.service_end_date,
463-
individual.service_start_date,
464-
individual.exit_date,
465-
self.id_number,
466-
individual.destination,
467-
individual.previous_class,
468-
individual.queue_size_at_arrival,
469-
individual.queue_size_at_departure)
466+
record = DataRecord(individual.id_number,
467+
individual.previous_class,
468+
self.id_number,
469+
individual.arrival_date,
470+
individual.service_start_date - individual.arrival_date,
471+
individual.service_start_date,
472+
individual.service_end_date - individual.service_start_date,
473+
individual.service_end_date,
474+
individual.exit_date - individual.service_end_date,
475+
individual.exit_date,
476+
individual.destination,
477+
individual.queue_size_at_arrival,
478+
individual.queue_size_at_departure)
470479
individual.data_records.append(record)
471480

472481
individual.arrival_date = False

ciw/server.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def __init__(self, node, id_number, start_date=0.0):
1818
self.busy_time = False
1919
self.total_time = False
2020
self.shift_end = False
21+
self.next_end_service_date = float('Inf')
2122

2223
@property
2324
def utilisation(self):

0 commit comments

Comments
 (0)