def __init__(self, rndgen, config): """ Create a new tasks generator. :param rndgen: (object) the multi-stream rnd number generator. :param config: (dict) the arrival rates configuration. """ # Arrival rates self.rates = { tsk: 1.0 / config[tsk]["parameters"]["m"] for tsk in TaskScope.concrete() } # Randomization self.rndgen = rndgen self.stream = { tsk: EventType.of(ActionScope.ARRIVAL, SystemScope.SYSTEM, tsk).value for tsk in TaskScope } self.lambda_tot = sum(self.rates[tsk] for tsk in TaskScope.concrete()) self.p_1 = self.rates[TaskScope.TASK_1] / self.lambda_tot # Events self.event_types = { tsk: EventType.of(ActionScope.ARRIVAL, SystemScope.SYSTEM, tsk) for tsk in TaskScope.concrete() } # State self.generated = {tsk: 0 for tsk in TaskScope.concrete()}
def __init__(self, config): """ Creates a new analytical solver. :param config: (Configuration) the system configuration. """ self.config = config self.markov_chain = None self.states_probabilities = None self.routing_probabilities = None self.solution = None self.__validate_markovianity() self.clt_n_servers = self.config["system"]["cloudlet"]["n_servers"] self.clt_controller_algorithm = ControllerAlgorithm[ self.config["system"]["cloudlet"]["controller_algorithm"]] self.clt_threshold = (self.config["system"]["cloudlet"]["threshold"] if self.clt_controller_algorithm is ControllerAlgorithm.ALGORITHM_2 else None) self.arrival_rates = { tsk: self.config["arrival"][tsk.name]["parameters"]["r"] for tsk in TaskScope.concrete() } self.service_rates = { sys: { tsk: self.config["system"][sys.name.lower()]["service"][ tsk.name]["parameters"]["r"] for tsk in TaskScope.concrete() } for sys in SystemScope.subsystems() } self.t_setup = self.config["system"]["cloud"]["setup"][ TaskScope.TASK_2.name]["parameters"]["m"]
def __init__(self, rndservice, idx): """ Create a new Server. :param rndservice: (RandomComponent) rnd component for the service process. :param idx: (int) the server index. """ # Server Index (used in randomization) self.idx = idx # Randomization self.rndservice = deepcopy(rndservice) for tsk in self.rndservice.str: self.rndservice.str[tsk] += self.idx # decoupling of each server randomness # State and important variables self.state = ServerState.IDLE # the state of the server (ServerState) self.task_type = None # the type of the task being served (TaskType) self.t_arrival = 0.0 # the last arrival time (float) (s) self.t_service = 0.0 # the last service time (float) (s) self.t_completion = 0.0 # the last completion time (float) (s) self.t_interruption = 0.0 # the last interruption time (float) (s) # Statistics (used by server selection rules) self.arrived = {tsk: 0 for tsk in TaskScope.concrete()} # total number of arrived tasks, by task type self.completed = {tsk: 0 for tsk in TaskScope.concrete()} # total number of completed tasks, by task type self.switched = {tsk: 0 for tsk in TaskScope.concrete()} # total number of interrupted tasks, by task type self.service = {tsk: 0 for tsk in TaskScope.concrete()} # total service time, by task type self.t_idle = 0.0 # the total idle time (float) (s)
def __init__(self, rndgen, config, state, metrics): """ Create a new Cloudlet. :param rndgen: (object) the multi-stream rnd number generator. :param config: (dict) the Cloudlet configuration. :param state: (dict) the Cloudlet state. :param metrics: (SimulationMetrics) the simulation metrics. """ # Randomization - Service self.rndservice = RandomComponent( gen=rndgen, str={ tsk: EventType.of(ActionScope.COMPLETION, SystemScope.CLOUDLET, tsk).value for tsk in TaskScope.concrete() }, var={tsk: config["service"][tsk]["distribution"] for tsk in TaskScope.concrete()}, par={tsk: config["service"][tsk]["parameters"] for tsk in TaskScope.concrete()}, ) # Servers self.n_servers = config["n_servers"] self.servers = [Server(self.rndservice, i) for i in range(self.n_servers)] self.server_selector = config["server_selection"].selector(self.servers) # State self.state = state # Controller controller_algorithm = config["controller_algorithm"] if controller_algorithm is ControllerAlgorithm.ALGORITHM_1: self.controller = ControllerAlgorithm1(self.state, self.n_servers) elif controller_algorithm is ControllerAlgorithm.ALGORITHM_2: self.threshold = config["threshold"] self.controller = ControllerAlgorithm2(self.state, self.n_servers, self.threshold) if not (0 <= self.threshold <= self.n_servers): raise ValueError( "Invalid threshold: should be >= 0 and <= n_servers, but threshold is {} and n_servers is {}".format( self.threshold, self.n_servers ) ) else: raise ValueError("Unrecognized controller algorithm type {}".format(controller_algorithm)) # Timing self.t_last_event = {tsk: 0.0 for tsk in TaskScope.concrete()} # Metrics self.metrics = metrics
def submit_arrival(self, tsk, t_now): """ Submit the arrival of a task. :param tsk: (TaskType) the type of the task. :param t_now: (float) the current time. :return: (float) the completion time. """ # Check correctness assert sum(self.state[tsk] for tsk in TaskScope.concrete()) < self.n_servers # Generate completion server_idx = self.server_selector.select_idle() if server_idx is None: raise RuntimeError("Cannot find server for arrival of task {} at time {}".format(tsk, t_now)) t_completion = self.servers[server_idx].submit_arrival(tsk, t_now) # Update metrics self.metrics.counters.arrived[SystemScope.CLOUDLET][tsk] += 1 self.metrics.counters.population_area[SystemScope.CLOUDLET][tsk] += ( t_now - self.t_last_event[tsk] ) * self.state[tsk] # Update state self.state[tsk] += 1 # Update timing self.t_last_event[tsk] = t_now return t_completion
def __init__(self, rndgen, config, metrics): """ Create a new system. :param rndgen: (object) the multi-stream rnd number generator. :param config: (dictionary) the System configuration. :param metrics: (SimulationMetrics) the simulation metrics. """ # State self.state = { sys: {tsk: 0 for tsk in TaskScope.concrete()} for sys in SystemScope.subsystems() } # Metrics self.metrics = metrics # Subsystem - Cloudlet self.cloudlet = Cloudlet(rndgen, config["cloudlet"], self.state[SystemScope.CLOUDLET], self.metrics) # Subsystem - Cloud self.cloud = Cloud(rndgen, config["cloud"], self.state[SystemScope.CLOUD], self.metrics)
def __init__(self, config, name="SIMULATION-CLOUD-CLOUDLET"): """ Create a new simulation. :param config: the configuration for the simulation. :param name: the name of the simulation. """ self.name = name # Configuration - General config_general = config["general"] self.mode = config_general["mode"] # Configuration - Transient Analysis if self.mode is SimulationMode.TRANSIENT_ANALYSIS: self.t_stop = config_general["t_stop"] # self.t_tran = 0 self.batches = INFINITE self.batchdim = 1 self.closed_door_condition = lambda: self.closed_door_condition_transient_analysis( ) self.print_progress = lambda: print_progress( self.calendar.get_clock(), self.t_stop, message="Clock: %d" % (self.calendar.get_clock())) # self.should_discard_transient_data = False # Configuration - Performance Analysis elif self.mode is SimulationMode.PERFORMANCE_ANALYSIS: self.t_stop = INFINITE # self.t_tran = config_general["t_tran"] self.batches = config_general["batches"] self.batchdim = config_general["batchdim"] self.closed_door_condition = lambda: self.closed_door_condition_performance_analysis( ) self.print_progress = lambda: print_progress( self.metrics.n_batches, self.batches, message="Clock: %d | Batches: %d | CurrentBatchSamples: %d" % (self.calendar.get_clock(), self.metrics.n_batches, self. metrics.curr_batchdim), ) # self.should_discard_transient_data = self.t_tran > 0.0 else: raise RuntimeError( "The current version supports only TRANSIENT_ANALYSIS and PERFORMANCE_ANALYSIS" ) # Configuration - Randomization self.rndgen = getattr(rndgen, config_general["random"]["generator"])( config_general["random"]["seed"]) # The simulation metrics self.metrics = SimulationMetrics(self.batchdim) self.confidence = config_general["confidence"] # Configuration - Tasks # Checks that the arrival process is Markovian (currently, the only one supported) if not all(variate is Variate.EXPONENTIAL for variate in [ config["arrival"][tsk]["distribution"] for tsk in TaskScope.concrete() ]): raise NotImplementedError( "The current version supports only exponential arrivals") self.taskgen = Taskgen(rndgen=self.rndgen, config=config["arrival"]) # Configuration - System (Cloudlet and Cloud) config_system = config["system"] self.system = System(rndgen=self.rndgen, config=config_system, metrics=self.metrics) # Configuration - Calendar # Notice that the calendar internally manages: # (i) event sorting, by occurrence time. # (ii) scheduling of only possible events, that are: # (ii.i) possible arrivals, i.e. arrivals with occurrence time lower than stop time. # (ii.ii) departures of possible arrivals. # (iii) unscheduling of events to ignore, e.g. completion in Cloudlet of interrupted tasks. self.calendar = Calendar(t_clock=0.0) # Sampling management self.sampling_file = None # Simulation management self.closed_door = False
def generate_report(self): """ Generate the simulation report. :return: (SimpleReport) the simulation report. """ r = Report(self.name) alpha = 1.0 - self.confidence # Report - General r.add("general", "mode", self.mode.name) if self.mode is SimulationMode.TRANSIENT_ANALYSIS: r.add("general", "t_stop", self.t_stop) elif self.mode is SimulationMode.PERFORMANCE_ANALYSIS: r.add("general", "batches", self.batches) r.add("general", "batchdim", self.batchdim) else: raise RuntimeError( "The current version supports only TRANSIENT_ANALYSIS and PERFORMANCE_ANALYSIS" ) r.add("general", "confidence", self.confidence) # Report - Randomization r.add("randomization", "generator", self.rndgen.__class__.__name__) r.add("randomization", "iseed", self.rndgen.get_initial_seed()) r.add("randomization", "modulus", self.rndgen.get_modulus()) r.add("randomization", "multiplier", self.rndgen.get_multiplier()) r.add("randomization", "streams", self.rndgen.get_nstreams()) # Report - Arrivals for tsk in TaskScope.concrete(): r.add("arrival", "arrival_{}_dist".format(tsk.name.lower()), Variate.EXPONENTIAL.name) r.add("arrival", "arrival_{}_rate".format(tsk.name.lower()), self.taskgen.rates[tsk]) for tsk in TaskScope.concrete(): r.add("arrival", "generated_{}".format(tsk.name.lower()), self.taskgen.generated[tsk]) # Report - System/Cloudlet r.add("system/cloudlet", "n_servers", self.system.cloudlet.n_servers) r.add("system/cloudlet", "controller_algorithm", self.system.cloudlet.controller.controller_algorithm.name) if self.system.cloudlet.controller.controller_algorithm is ControllerAlgorithm.ALGORITHM_2: r.add("system/cloudlet", "threshold", self.system.cloudlet.threshold) for tsk in TaskScope.concrete(): r.add( "system/cloudlet", "service_{}_dist".format(tsk.name.lower()), self.system.cloudlet.rndservice.var[tsk].name, ) if self.system.cloudlet.rndservice.var[tsk] is Variate.EXPONENTIAL: r.add( "system/cloudlet", "service_{}_rate".format(tsk.name.lower()), 1.0 / self.system.cloudlet.rndservice.par[tsk]["m"], ) else: for p in self.system.cloudlet.rndservice.par[tsk]: r.add( "system/cloudlet", "service_{}_param_{}".format(tsk.name.lower(), p), self.system.cloudlet.rndservice.par[tsk][p], ) # Report - System/Cloud for tsk in TaskScope.concrete(): r.add("system/cloud", "service_{}_dist".format(tsk.name.lower()), self.system.cloud.rndservice.var[tsk].name) if self.system.cloud.rndservice.var[tsk] is Variate.EXPONENTIAL: r.add( "system/cloud", "service_{}_rate".format(tsk.name.lower()), 1.0 / self.system.cloud.rndservice.par[tsk]["m"], ) else: for p in self.system.cloud.rndservice.par[tsk]: r.add( "system/cloud", "service_{}_param_{}".format(tsk.name.lower(), p), self.system.cloud.rndservice.par[tsk][p], ) for tsk in TaskScope.concrete(): r.add("system/cloud", "setup_{}_dist".format(tsk.name.lower()), self.system.cloud.rndsetup.var[tsk].name) for p in self.system.cloud.rndsetup.par[tsk]: r.add( "system/cloud", "service_{}_param_{}".format(tsk.name.lower(), p), self.system.cloud.rndsetup.par[tsk][p], ) # Report - Execution r.add("execution", "clock", self.calendar.get_clock()) r.add("execution", "collected_samples", self.metrics.n_samples) r.add("execution", "collected_batches", self.metrics.n_batches) # Report - State for sys in sorted(self.system.state, key=lambda x: x.name): for tsk in sorted(self.system.state[sys], key=lambda x: x.name): r.add("state", "{}_{}".format(sys.name.lower(), tsk.name.lower()), self.system.state[sys][tsk]) # Report - Statistics for metric in sorted(self.metrics.performance_metrics.__dict__): for sys in sorted(SystemScope, key=lambda x: x.name): for tsk in sorted(TaskScope, key=lambda x: x.name): r.add( "statistics", "{}_{}_{}_mean".format(metric, sys.name.lower(), tsk.name.lower()), getattr(self.metrics.performance_metrics, metric)[sys][tsk].mean(), ) r.add( "statistics", "{}_{}_{}_sdev".format(metric, sys.name.lower(), tsk.name.lower()), getattr(self.metrics.performance_metrics, metric)[sys][tsk].sdev(), ) r.add( "statistics", "{}_{}_{}_cint".format(metric, sys.name.lower(), tsk.name.lower()), getattr(self.metrics.performance_metrics, metric)[sys][tsk].cint(alpha), ) return r
for tsk in TaskScope: header.append("{}_{}_{}".format(counter, sys.name.lower(), tsk.name.lower())) sample.append(getattr(self.counters, counter)[sys][tsk]) for performance_metric in sorted(self.performance_metrics.__dict__): for sys in SystemScope: for tsk in TaskScope: header.append("{}_{}_{}".format(performance_metric, sys.name.lower(), tsk.name.lower())) sample.append(getattr(self.performance_metrics, performance_metric)[sys][tsk]) data = [sample] save_csv(filename, header, data, append, skip_header) if __name__ == "__main__": s = Sample(0) s.counters.arrived[SystemScope.CLOUDLET][TaskScope.TASK_1] = 1 s.counters.arrived[SystemScope.CLOUDLET][TaskScope.TASK_2] = 2 s.counters.arrived[SystemScope.CLOUD][TaskScope.TASK_1] = 3 s.counters.arrived[SystemScope.CLOUD][TaskScope.TASK_2] = 4 for tsk in TaskScope.concrete(): s.counters.arrived[SystemScope.SYSTEM][tsk] = sum( s.counters.arrived[sys][tsk] for sys in SystemScope.subsystems() ) for tsk in TaskScope.concrete(): print(s.counters.arrived[SystemScope.SYSTEM][tsk])
def generate_report(self): """ Generate the solver report. :return: (SimpleReport) the solver report. """ r = Report("ANALYTICAL-SOLUTION") # Report - Arrivals for tsk in TaskScope.concrete(): r.add("arrival", "arrival_{}_dist".format(tsk.name.lower()), Variate.EXPONENTIAL.name) r.add("arrival", "arrival_{}_rate".format(tsk.name.lower()), self.arrival_rates[tsk]) # Report - System/Cloudlet r.add("system/cloudlet", "n_servers", self.clt_n_servers) r.add("system/cloudlet", "controller_algorithm", self.clt_controller_algorithm.name) if self.clt_controller_algorithm is ControllerAlgorithm.ALGORITHM_2: r.add("system/cloudlet", "threshold", self.clt_threshold) for tsk in TaskScope.concrete(): r.add("system/cloudlet", "service_{}_dist".format(tsk.name.lower()), Variate.EXPONENTIAL.name) r.add( "system/cloudlet", "service_{}_rate".format(tsk.name.lower()), self.service_rates[SystemScope.CLOUDLET][tsk], ) # Report - System/Cloud for tsk in TaskScope.concrete(): r.add("system/cloud", "service_{}_dist".format(tsk.name.lower()), Variate.EXPONENTIAL.name) r.add("system/cloud", "service_{}_rate".format(tsk.name.lower()), self.service_rates[SystemScope.CLOUD][tsk]) r.add("system/cloud", "setup_{}_dist".format(TaskScope.TASK_2.name.lower()), Variate.EXPONENTIAL.name) r.add("system/cloud", "service_{}_param_m".format(TaskScope.TASK_2.name.lower()), self.t_setup) # Report - Statistics for performance_metric in sorted( self.solution.performance_metrics.__dict__): for sys in sorted(SystemScope, key=lambda x: x.name): for tsk in sorted(TaskScope, key=lambda x: x.name): r.add( "statistics", "{}_{}_{}_mean".format(performance_metric, sys.name.lower(), tsk.name.lower()), getattr(self.solution.performance_metrics, performance_metric)[sys][tsk], ) # Report - States Probability for state in sorted(self.states_probabilities): r.add("states probability", state, self.states_probabilities[state]) # Report - Routing Probability for probability, value in self.routing_probabilities.items(): r.add("routing probability", probability, value) return r
from pydes.core.rnd.rndgen import MarcianiMultiStream from pydes.core.rnd.rndvar import Variate from pydes.core.simulation.model.scope import TaskScope rndgen = MarcianiMultiStream() rndparams = { TaskScope.TASK_1: {"distribution": "EXPONENTIAL", "distribution_params": {"m": 0.5}}, TaskScope.TASK_2: {"distribution": "EXPONENTIAL", "distribution_params": {"m": 0.8}}, } variates = { tsk: lambda u: Variate[rndparams[tsk]["distribution"]].vargen.generate(u=u, **rndparams[tsk]["distribution_params"]) for tsk in TaskScope.concrete() } def get_service_time(tsk): return variates[tsk](u=rndgen) if __name__ == "__main__": values = {tsk: [] for tsk in TaskScope.concrete()} for tsk in TaskScope.concrete(): for i in range(10): t = get_service_time(tsk) values[tsk].append(t)
def is_idle(self): """ Check weather the Cloudlet is idle or not. :return: True, if the Cloudlet is idle; False, otherwise. """ return sum(self.state[tsk] for tsk in TaskScope.concrete()) == 0
def _compute_global_counters(self, t_now): """ Updates aggregated counters (e.g. system global counters) using task-scoped and subsystem-scoped counters (e.g. counter for task 1 in Cloudlet). :param t_now (float) the current time, :return: None """ # Compute counters for each task class in system # * arrived_system = arrived_cloudlet + arrived_cloud # * completed_system = completed_cloudlet + completed_cloud # * service_system = service_cloudlet + service_cloud # * switched_system = switched_cloudlet # * switched_completed_system = switched_completed_cloud # * switched_service_system = switched_service_cloudlet + switched_service_cloud # * switched_service_lost_system = switched_service_lost_cloudlet + switched_service_lost_cloud # * population_area_system = population_area_cloudlet + population_area_lost_cloud for tsk in TaskScope.concrete(): self.counters.arrived[SystemScope.SYSTEM][tsk] = sum( self.counters.arrived[sys][tsk] for sys in SystemScope.subsystems()) self.counters.completed[SystemScope.SYSTEM][tsk] = sum( self.counters.completed[sys][tsk] for sys in SystemScope.subsystems()) self.counters.service[SystemScope.SYSTEM][tsk] = sum( self.counters.service[sys][tsk] for sys in SystemScope.subsystems()) self.counters.switched[SystemScope.SYSTEM][ tsk] = self.counters.switched[SystemScope.CLOUDLET][tsk] self.counters.switched_completed[SystemScope.SYSTEM][ tsk] = self.counters.switched_completed[SystemScope.CLOUD][tsk] self.counters.switched_service[SystemScope.SYSTEM][tsk] = sum( self.counters.switched_service[sys][tsk] for sys in SystemScope.subsystems()) self.counters.switched_service_lost[SystemScope.SYSTEM][tsk] = sum( self.counters.switched_service_lost[sys][tsk] for sys in SystemScope.subsystems()) self.counters.population_area[SystemScope.SYSTEM][tsk] = sum( self.counters.population_area[sys][tsk] for sys in SystemScope.subsystems()) # Compute counters for global tasks # * arrived_global = arrived_task_1 + arrived_task_2 # * completed_global = completed_task_1 + completed_task_2 # * service_global = service_task_1 + service_task_2 # * switched_global = switched_task_1 + switched_task_2 # * switched_completed_global = switched_completed_task_1 + switched_completed_task_2 # * switched_service_global = switched_service_task_1 + switched_service_task_2 # * switched_service_lost_global = switched_service_lost_task_1 + switched_service_lost_task_2 # * population_area_global = population_area_task_1 + population_area_task_2 for sys in SystemScope: self.counters.arrived[sys][TaskScope.GLOBAL] = sum( self.counters.arrived[sys][tsk] for tsk in TaskScope.concrete()) self.counters.completed[sys][TaskScope.GLOBAL] = sum( self.counters.completed[sys][tsk] for tsk in TaskScope.concrete()) self.counters.service[sys][TaskScope.GLOBAL] = sum( self.counters.service[sys][tsk] for tsk in TaskScope.concrete()) self.counters.switched[sys][TaskScope.GLOBAL] = sum( self.counters.switched[sys][tsk] for tsk in TaskScope.concrete()) self.counters.switched_completed[sys][TaskScope.GLOBAL] = sum( self.counters.switched_completed[sys][tsk] for tsk in TaskScope.concrete()) self.counters.switched_service[sys][TaskScope.GLOBAL] = sum( self.counters.switched_service[sys][tsk] for tsk in TaskScope.concrete()) self.counters.switched_service_lost[sys][TaskScope.GLOBAL] = sum( self.counters.switched_service_lost[sys][tsk] for tsk in TaskScope.concrete()) self.counters.population_area[sys][TaskScope.GLOBAL] = sum( self.counters.population_area[sys][tsk] for tsk in TaskScope.concrete())