diff --git a/src/main.py b/src/main.py index a70096c..b7545e1 100644 --- a/src/main.py +++ b/src/main.py @@ -1,14 +1,21 @@ #!/bin/python3 -import random as rd +import random from heapq import heappush, heappop class Event: def __init__(self, event_type, additional_options): self.event_type = event_type if event_type == "request": - self.category = additional_options['category'] + self.request = additional_options['request'] elif event_type == "router_finish": self.request = additional_options['request'] + elif event_type == "process_finish": + self.request = additional_options['request'] + +class Request: + def __init__(self, category, arrival_time): + self.category = category + self.arrival_time = arrival_time class Simulation: @@ -16,6 +23,7 @@ class Simulation: # C clusters of K servers self.C = C self.K = 12 // C + self.occupied_servers = [0] * self.C # service rate exponential distribution parameter service_rates = {1: 4/20, 2:7/20, 3:10/20, 6:14/20} self.service_rate = service_rates[C] @@ -26,33 +34,103 @@ class Simulation: self.router_state = 'idle' # 'idle', 'processing', 'blocked' - self.event_queue = Queue() # (arrival_time, category) + self.event_queue = [] # (time, Event) self.current_time = 0.0 + self.router_queue = [] + self.total_requests = 0 self.lost_requests = 0 + self.loss_rate = 0 self.response_times = [] - def next_request(): + def next_request(self): # exponential distribution, parameter λ interval = random.expovariate(self.lambda_val) new_time = self.current_time + interval arrival_time = new_time - category = rd.randint(1, C) if C>1 else 1 + category = random.randint(0, self.C-1) if self.C>1 else 0 - request = Event("request", {"category": category}) + request = Request(category, arrival_time) + request_event = Event("request", {"request": request}) - heappush(self.event_queue, (arrival_time, request)) + heappush(self.event_queue, (arrival_time, request_event)) - def router_process(request): - self.router_state = 'processing' - router_finish = Event("router_finish", {"request": request}) - heappush(self.event_queue, (arrival_time, router_finish)) + def handle_request(self, request): + self.total_requests += 1 + if len(self.router_queue) == 0 and self.router_state == "idle": + self.router_process(request) + elif ((len(self.router_queue) + + (self.router_state == "processing")) < 100): + self.router_queue.append(request) + else: + self.lost_requests += 1 + self.loss_rate = self.lost_requests / self.total_requests + if self.loss_rate > 0.05 : + raise ValueError("lossrate too high") - def process_request(self, request, current_time): + def router_process(self, request): + if self.router_state == "idle": + self.router_state = 'processing' + router_finish = Event("router_finish", {"request": request}) + finish_time = self.current_time + self.router_processing_time + heappush(self.event_queue, (finish_time, router_finish)) + else: + raise RuntimeError("shouldn't reach this branch") + + + def router_process_finish(self, request): + # send the request to a free server + if self.occupied_servers[request.category] < self.K: + self.router_state = "idle" + self.occupied_servers[request.category] += 1 + self.process_request(request) + else: + self.router_state = "blocked" + self.router_queue.insert(0, request) + + # router process next request + self.router_process_next() + + def router_process_next(self): + if (len(self.router_queue) > 0) and (self.router_state == "idle"): + self.router_process(self.router_queue.pop(0)) + + def process_request(self, request): interval = random.expovariate(self.service_rate) - finish = current_time + interval + finish_time = self.current_time + interval + process_finish = Event("process_finish", {"request": request}) + heappush(self.event_queue, (finish_time, process_finish)) + + def process_request_finish(self, request): + self.response_times.append(self.current_time - request.arrival_time) + self.occupied_servers[request.category] -= 1 + + if (self.router_state == "blocked") and (request.category == self.router_queue[0].category): + self.process_request(self.router_queue.pop(0)) + self.occupied_servers[request.category] += 1 + self.router_state = "idle" + self.router_process_next() + + + def run(self, max_time): + # first request + self.next_request() + + while (self.current_time <= max_time): + current_event = heappop(self.event_queue) + self.current_time = current_event[0] + match current_event[1].event_type: + case "request": + self.next_request() + self.handle_request(current_event[1].request) + case "router_finish": + self.router_process_finish(current_event[1].request) + case "process_finish": + self.process_request_finish(current_event[1].request) + case _ : + raise RuntimeError("shouldn't reach this branch") +