def test_heat_conservation_simple(self): # Dimensions of the cubes cubes_dimensions = Dimensions(x=3, z=3, y=3) # Cube material cuboid_material = SMSilicon() # Cuboid initial temperature system_initial_temperature = 273.15 + 45 # Definition of the CPU shape and materials scene_definition = { # Cube 0: (cuboid_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=cubes_dimensions)) } # Edge size pf 1 mm cube_edge_size = 0.001 cubed_space = Model(material_cubes=scene_definition, cube_edge_size=cube_edge_size, environment_properties=None, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=system_initial_temperature, environment_temperature=None) max_temperature_vector = [] min_temperature_vector = [] number_of_iterations = 10000 for i in range(number_of_iterations): # Apply energy over the cubed space initial_state = cubed_space.apply_energy( actual_state=initial_state, amount_of_time=0.001) temperature_over_energy_application = cubed_space.obtain_temperature( actual_state=initial_state) min_temperature_one = min( obtain_min_temperature( temperature_over_energy_application).values()) max_temperature_one = max( obtain_max_temperature( temperature_over_energy_application).values()) min_temperature_vector.append(min_temperature_one) max_temperature_vector.append(max_temperature_one) assert (self.float_equal(min(min_temperature_vector), system_initial_temperature, error=0.1)) assert (self.float_equal(max(max_temperature_vector), system_initial_temperature, error=0.1))
def generate_board_temperature_evolution_2d_video(schedule_result: RawSimulationResult, title: Optional[str] = None) -> FuncAnimation: """ Generate a 2D animation of the evolution in the temperature of the processor :param schedule_result: Result of the simulation :param title: Plot title :return: plot """ min_simulation_value = min(min(obtain_min_temperature(i).values()) for i in schedule_result.temperature_measures.values()) max_simulation_value = max(max(obtain_max_temperature(i).values()) for i in schedule_result.temperature_measures.values()) return generate_video_2d_heat_map( schedule_result.temperature_measures, min_temperature=min_simulation_value, max_temperature=max_simulation_value, axis="Z", location_in_axis=1, delay_between_frames_ms=100)
def generate_component_hotspots_plot( schedule_result: RawSimulationResult, start_time: float = 0, end_time: Optional[float] = None, title: Optional[str] = None, units: Literal["KELVIN", "CELSIUS"] = "KELVIN") -> Figure: """ Generate a graphic with the temperature of the hotspots in the components (cores and board) over time :param units: Units where the temperature will be expressed :param schedule_result: Result of the simulation :param start_time: Plot start time in seconds :param end_time: Plot end time in seconds :param title: Plot title :return: plot """ cpu_ids: List[int] = sorted(schedule_result.job_sections_execution.keys()) fig, ax = pyplot.subplots(nrows=len(cpu_ids) + 1) measures_times = list(sorted(schedule_result.temperature_measures.keys())) max_temperature_list = [ obtain_max_temperature(schedule_result.temperature_measures[i]) for i in measures_times ] # min_temperature = 0 for cpu_id in cpu_ids + [len(cpu_ids)]: max_temperature_list_cpu_i = [ i[cpu_id] - (0 if units == "KELVIN" else 273.15) for i in max_temperature_list ] ax[cpu_id].plot(measures_times, max_temperature_list_cpu_i) ax[cpu_id].set_xlim(start_time, end_time) # Set label ax[cpu_id].set_xlabel(f'Time (s)') if cpu_id != len(cpu_ids): ax[cpu_id].set_ylabel( f'CPU {cpu_id} \n Temperature ({"K" if units == "KELVIN" else "ºC"})' ) else: ax[cpu_id].set_ylabel( f'Board \n Temperature ({"K" if units == "KELVIN" else "ºC"})') # Set uniform y_lim max_y = max(ax[i].get_ylim()[1] for i in range(len(cpu_ids) + 1)) min_y = min(ax[i].get_ylim()[0] for i in range(len(cpu_ids) + 1)) for i in range(len(cpu_ids) + 1): ax[i].set_ylim(min_y, max_y) # Set title if title is not None: fig.suptitle(title) # Adjust layout fig.tight_layout() return fig
def test_external_conduction_plot_3d_stacked(self): # Configuration # Size of the simulation step simulation_step = 0.1 # Major cycle in seconds major_cycle = 1.0 # Temperature of the system (Celsius degrees) system_temperature = 45 number_of_cores = 1 # Dimensions of the core core_dimensions = Dimensions(x=10, z=2, y=10) # Material of the core core_material = SMSilicon() # Material of the board board_material = SMCooper() # Environment initial temperature environment_temperature = 273.15 + system_temperature # CPU distribution # XXXXXXX # XX0X3XX # XX1X4XX # XX2X5XX # XXXXXXX # Definition of the CPU shape and materials cpu_definition = { # Cores 0: (core_material, Cuboid(location=Location(x=20, z=2, y=10), dimensions=core_dimensions)), # Board 1: (board_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=Dimensions(x=70, z=2, y=70))) } # Edge size pf 0.5 mm cube_edge_size = 0.001 # Environment properties environment_properties = FEAirFree() external_heat_generators_dynamic_power = { 0: create_energy_applicator( (core_material, Cuboid(location=Location(x=20, z=0, y=10), dimensions=Dimensions(x=10, z=2, y=10))), watts_to_apply=10, cube_edge_size=cube_edge_size) } # Generate cubed space cubed_space = Model(material_cubes=cpu_definition, cube_edge_size=cube_edge_size, external_temperature_booster_points= external_heat_generators_dynamic_power, internal_temperature_booster_points={}, environment_properties=environment_properties, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=environment_temperature, environment_temperature=environment_temperature) # Initial temperatures temperature_measures: Dict[float, Dict[int, PhysicalCuboid]] = {} temperature_over_before_zero_seconds = cubed_space.obtain_temperature( actual_state=initial_state) temperature_measures[0.0] = temperature_over_before_zero_seconds actual_applied_externally_energy_points = {0} # Apply energy over the cubed space for i in range(round(major_cycle / simulation_step)): initial_state = cubed_space.apply_energy( actual_state=initial_state, external_energy_application_points= actual_applied_externally_energy_points, internal_energy_application_points=set(), amount_of_time=simulation_step) temperature = cubed_space.obtain_temperature( actual_state=initial_state) temperature_measures[i * simulation_step] = temperature # Display temperatures for i in range(round(major_cycle / simulation_step)): time_to_display = i * simulation_step min_temperature = min( obtain_min_temperature( temperature_measures[time_to_display]).values()) max_temperature = max( obtain_max_temperature( temperature_measures[time_to_display]).values()) plot_3d = plot_3d_heat_map_temperature( temperature_measures[time_to_display], min_temperature=min_temperature, max_temperature=max_temperature) plot_3d.show()
def test_internal_conduction_plot(self): # Dimensions of the cubes cubes_dimensions = Dimensions(x=2, z=2, y=2) # Cube 0 material cube_0_material = SMCooper() # Core initial temperature cube_0_initial_temperature = 273.15 + 65 # Board initial temperature environment_temperature = 273.15 + 25 # Min simulation value min_simulation_value = cube_0_initial_temperature - 10 max_simulation_value = cube_0_initial_temperature + 10 # Definition of the CPU shape and materials scene_definition = { # Cores 0: (cube_0_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=cubes_dimensions)) } # Edge size pf 1 mm cube_edge_size = 0.001 # Environment properties environment_properties = FEAirForced() cubed_space = Model(material_cubes=scene_definition, cube_edge_size=cube_edge_size, environment_properties=environment_properties, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=environment_temperature, material_cubes_temperatures={0: cube_0_initial_temperature}, environment_temperature=environment_temperature) # Initial temperatures temperature_over_before_zero_seconds = cubed_space.obtain_temperature( actual_state=initial_state) # Apply energy over the cubed space initial_state = cubed_space.apply_energy(actual_state=initial_state, amount_of_time=0.9) temperature_over_before_half_second = cubed_space.obtain_temperature( actual_state=initial_state) # Apply energy over the cubed space initial_state = cubed_space.apply_energy(actual_state=initial_state, amount_of_time=0.9) temperature_over_before_one_second = cubed_space.obtain_temperature( actual_state=initial_state) # Zero seconds plot_3d_heat_map_temperature(temperature_over_before_zero_seconds, min_temperature=min_simulation_value, max_temperature=max_simulation_value) min_temperature = obtain_min_temperature( temperature_over_before_zero_seconds) max_temperature = obtain_max_temperature( temperature_over_before_zero_seconds) print("Temperature before 0 seconds: min", min_temperature, ", max", max_temperature) # Half second plot_3d_heat_map_temperature(temperature_over_before_half_second, min_temperature=min_simulation_value, max_temperature=max_simulation_value) min_temperature = obtain_min_temperature( temperature_over_before_half_second) max_temperature = obtain_max_temperature( temperature_over_before_half_second) print("Temperature before 0.5 seconds: min", min_temperature, ", max", max_temperature) # One second plot_3d_heat_map_temperature(temperature_over_before_one_second, min_temperature=min_simulation_value, max_temperature=max_simulation_value) min_temperature = obtain_min_temperature( temperature_over_before_one_second) max_temperature = obtain_max_temperature( temperature_over_before_one_second) print("Temperature before 1 second: min", min_temperature, ", max", max_temperature)
def test_processor_heat_generation_plot(self): # Dimensions of the core core_dimensions = Dimensions(x=10, z=2, y=10) # Material of the core core_material = SMSilicon() # Material of the board board_material = SMCooper() # Core initial temperature # core_initial_temperature = 273.15 + 65 core_0_initial_temperature = 273.15 + 25 core_1_initial_temperature = 273.15 + 25 core_2_initial_temperature = 273.15 + 25 core_3_initial_temperature = 273.15 + 25 # Board initial temperature board_initial_temperature = 273.15 + 25 # Environment initial temperature environment_temperature = 273.15 + 25 # Min simulation value max_simulation_value = 273.15 + 80 min_simulation_value = 273.15 + 20 # Definition of the CPU shape and materials cpu_definition = { # Cores 0: (core_material, Cuboid(location=Location(x=10, z=2, y=10), dimensions=core_dimensions)), 1: (core_material, Cuboid(location=Location(x=30, z=2, y=10), dimensions=core_dimensions)), 2: (core_material, Cuboid(location=Location(x=10, z=2, y=30), dimensions=core_dimensions)), 3: (core_material, Cuboid(location=Location(x=30, z=2, y=30), dimensions=core_dimensions)), # Board 4: (board_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=Dimensions(x=50, z=2, y=50))) } # Edge size pf 1 mm cube_edge_size = 0.001 # Environment properties environment_properties = FEAirFree() # CPU energy consumption configuration # Dynamic power = dynamic_alpha * F^3 + dynamic_beta # Leakage power = current temperature * 2 * leakage_delta + leakage_alpha leakage_alpha: float = 0.001 leakage_delta: float = 0.1 dynamic_alpha: float = 19 * 1000**-3 dynamic_beta: float = 2 cpu_frequency: float = 1000 watts_to_apply = dynamic_alpha * (cpu_frequency**3) + dynamic_beta # External heat generators external_heat_generators = { # Dynamic power 0: create_energy_applicator( (core_material, Cuboid(location=Location(x=10, z=2, y=10), dimensions=core_dimensions)), watts_to_apply=watts_to_apply, cube_edge_size=cube_edge_size), # Leakage power 1: create_energy_applicator( (core_material, Cuboid(location=Location(x=10, z=2, y=10), dimensions=core_dimensions)), watts_to_apply=leakage_alpha, cube_edge_size=cube_edge_size), 2: create_energy_applicator( (core_material, Cuboid(location=Location(x=30, z=2, y=10), dimensions=core_dimensions)), watts_to_apply=leakage_alpha, cube_edge_size=cube_edge_size), 3: create_energy_applicator( (core_material, Cuboid(location=Location(x=10, z=2, y=30), dimensions=core_dimensions)), watts_to_apply=leakage_alpha, cube_edge_size=cube_edge_size), 4: create_energy_applicator( (core_material, Cuboid(location=Location(x=30, z=2, y=30), dimensions=core_dimensions)), watts_to_apply=leakage_alpha, cube_edge_size=cube_edge_size) } # Internal heat generators internal_heat_generators = { 0: TMInternal(cuboid=Cuboid(location=Location(x=10, z=2, y=10), dimensions=core_dimensions), boostRateMultiplier=leakage_delta), 1: TMInternal(cuboid=Cuboid(location=Location(x=30, z=2, y=10), dimensions=core_dimensions), boostRateMultiplier=leakage_delta), 2: TMInternal(cuboid=Cuboid(location=Location(x=10, z=2, y=30), dimensions=core_dimensions), boostRateMultiplier=leakage_delta), 3: TMInternal(cuboid=Cuboid(location=Location(x=30, z=2, y=30), dimensions=core_dimensions), boostRateMultiplier=leakage_delta) } # Generate cubed space cubed_space = Model( material_cubes=cpu_definition, cube_edge_size=cube_edge_size, external_temperature_booster_points=external_heat_generators, internal_temperature_booster_points=internal_heat_generators, environment_properties=environment_properties, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=environment_temperature, material_cubes_temperatures={ 0: core_0_initial_temperature, 1: core_1_initial_temperature, 2: core_2_initial_temperature, 3: core_3_initial_temperature, 4: board_initial_temperature }, environment_temperature=environment_temperature) temperatures_vector = [] # Initial temperatures temperature_over_before_zero_seconds = cubed_space.obtain_temperature( actual_state=initial_state) temperatures_vector.append(temperature_over_before_zero_seconds) # Apply energy over the cubed space number_of_iterations = 10 for i in range(number_of_iterations): initial_state = cubed_space.apply_energy( actual_state=initial_state, external_energy_application_points={0}, # {0, 1, 2, 3, 4}, # internal_energy_application_points={0, 1, 2, 3}, amount_of_time=0.5) temperature = cubed_space.obtain_temperature( actual_state=initial_state) temperatures_vector.append(temperature) for i, temperature in enumerate(temperatures_vector): # Zero seconds plot_3d_heat_map_temperature( temperature, min_temperature=min_simulation_value, max_temperature=max_simulation_value).show() plot_2d_heat_map(temperature, min_temperature=min_simulation_value, max_temperature=max_simulation_value, axis="Z", location_in_axis=1).show() min_temperature = obtain_min_temperature(temperature) max_temperature = obtain_max_temperature(temperature) print("Temperature before", i * 0.5, "seconds: min", min_temperature, ", max", max_temperature)
def test_internal_conduction_with_convection(self): # Dimensions of the cubes cubes_dimensions = Dimensions(x=3, z=3, y=3) # Cube 0 material cuboid_material = SMSilicon() # Core initial temperature cuboid_initial_temperature = 273.15 + 65 # Board initial temperature environment_temperature = 273.15 + 25 # Definition of the CPU shape and materials scene_definition = { # Cores 0: (cuboid_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=cubes_dimensions)) } # Edge size pf 1 mm cube_edge_size = 0.001 # Environment properties environment_properties = FEAirForced() cubed_space = Model(material_cubes=scene_definition, cube_edge_size=cube_edge_size, environment_properties=environment_properties, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=environment_temperature, material_cubes_temperatures={0: cuboid_initial_temperature}, environment_temperature=environment_temperature) # Apply energy over the cubed space initial_state = cubed_space.apply_energy(actual_state=initial_state, amount_of_time=0.5) temperature_over_before_half_second = cubed_space.obtain_temperature( actual_state=initial_state) # Apply energy over the cubed space initial_state = cubed_space.apply_energy(actual_state=initial_state, amount_of_time=0.5) temperature_over_before_one_second = cubed_space.obtain_temperature( actual_state=initial_state) # Half second min_temperature_half = obtain_min_temperature( temperature_over_before_half_second) max_temperature_half = obtain_max_temperature( temperature_over_before_half_second) min_temperature_half = min(min_temperature_half.values()) max_temperature_half = max(max_temperature_half.values()) # One second min_temperature_one = obtain_min_temperature( temperature_over_before_one_second) max_temperature_one = obtain_max_temperature( temperature_over_before_one_second) min_temperature_one = min(min_temperature_one.values()) max_temperature_one = max(max_temperature_one.values()) assert (environment_temperature <= min_temperature_half <= cuboid_initial_temperature and max_temperature_half <= cuboid_initial_temperature) assert (environment_temperature <= min_temperature_one <= cuboid_initial_temperature and max_temperature_one <= cuboid_initial_temperature) assert (min_temperature_one <= min_temperature_half and max_temperature_one <= max_temperature_half)
def test_processor_heat_conservation(self): # Dimensions of the core core_dimensions = Dimensions(x=10, z=2, y=10) # Material of the core core_material = SMSilicon() # Material of the board board_material = SMCooper() # System initial temperature system_initial_temperature = 273.15 + 25 # Core initial temperature core_initial_temperature = system_initial_temperature # Board initial temperature board_initial_temperature = system_initial_temperature # Environment initial temperature environment_temperature = system_initial_temperature # Definition of the CPU shape and materials cpu_definition = { # Cores 0: (core_material, Cuboid(location=Location(x=10, z=2, y=10), dimensions=core_dimensions)), 1: (core_material, Cuboid(location=Location(x=30, z=2, y=10), dimensions=core_dimensions)), 2: (core_material, Cuboid(location=Location(x=10, z=2, y=30), dimensions=core_dimensions)), 3: (core_material, Cuboid(location=Location(x=30, z=2, y=30), dimensions=core_dimensions)), # Board 4: (board_material, Cuboid(location=Location(x=0, z=0, y=0), dimensions=Dimensions(x=50, z=2, y=50))) } # Edge size pf 1 mm cube_edge_size = 0.001 # Environment properties environment_properties = FEAirFree() # Generate cubed space cubed_space = Model(material_cubes=cpu_definition, cube_edge_size=cube_edge_size, external_temperature_booster_points={}, internal_temperature_booster_points={}, environment_properties=environment_properties, simulation_precision="HIGH") initial_state = cubed_space.create_initial_state( default_temperature=environment_temperature, material_cubes_temperatures={ 0: core_initial_temperature, 1: core_initial_temperature, 2: core_initial_temperature, 3: core_initial_temperature, 4: board_initial_temperature }, environment_temperature=environment_temperature) min_max_temperatures_vector: List[Tuple[float, float, float]] = [ (0.0, core_initial_temperature, core_initial_temperature) ] # Initial temperatures temperature_over_before_zero_seconds = cubed_space.obtain_temperature( actual_state=initial_state) min_temperature = obtain_min_temperature( temperature_over_before_zero_seconds) max_temperature = obtain_max_temperature( temperature_over_before_zero_seconds) min_max_temperatures_vector.append((0.0, min(min_temperature.values()), max(max_temperature.values()))) # Apply energy over the cubed space number_of_iterations = 100 for i in range(number_of_iterations): initial_state = cubed_space.apply_energy( actual_state=initial_state, amount_of_time=0.001) temperature = cubed_space.obtain_temperature( actual_state=initial_state) min_temperature = obtain_min_temperature(temperature) max_temperature = obtain_max_temperature(temperature) min_max_temperatures_vector.append( (0.0, min(min_temperature.values()), max(max_temperature.values()))) assert all(i > system_initial_temperature - 0.1 for _, i, _ in min_max_temperatures_vector) assert all(i < system_initial_temperature + 0.1 for _, _, i in min_max_temperatures_vector)
def _execute_centralized_scheduler_simulation( jobs: List[Job], tasks: TaskSet, processor_definition: Processor, environment_specification: Environment, scheduler: CentralizedScheduler, simulation_options: SimulationConfiguration, simulation_start_time: float, simulation_end_time: float) -> RawSimulationResult: """ Run a simulation using a centralized scheduler :param jobs: Jobs in the system :param simulation_start_time: Time in seconds where the system start to make decisions. Time 0 is the start of the first major cycle :param simulation_end_time: Time in seconds since the start of the first major cycle where the simulation ends. :param tasks: Group of tasks in the system :param processor_definition: Definition of the CPU to use :param environment_specification: Specification of the environment :param scheduler: Centralized scheduler to use :param simulation_options: Options of the simulation :return: Simulation result """ # Possible frequencies # As we are simulating with a centralized scheduler, only frequencies possibles in all cores are available available_frequencies = Set.intersection(*[ i.core_type.available_frequencies for i in processor_definition.cores_definition.values() ]) if len(available_frequencies) == 0: return RawSimulationResult( have_been_scheduled=False, scheduler_acceptance_error_message= "at least one frequency must be shared by all" + " cores in a centralized scheduler simulation", job_sections_execution={}, cpus_frequencies={}, scheduling_points=[], temperature_measures={}, hard_real_time_deadline_missed_stack_trace=None, memory_usage_record=None) # Unit mesh division if simulation_options.processor_mesh_division < 1: return RawSimulationResult( have_been_scheduled=False, scheduler_acceptance_error_message= "mesh division must be greater than 0", job_sections_execution={}, cpus_frequencies={}, scheduling_points=[], temperature_measures={}, hard_real_time_deadline_missed_stack_trace=None, memory_usage_record=None) # Number of cpus number_of_cpus = len(processor_definition.cores_definition) # Check if CPU ids go from 0 to number_of_tasks - 1 cpus_ids_corrects: bool = all( 0 <= i < number_of_cpus for i in processor_definition.cores_definition.keys()) if not cpus_ids_corrects: return RawSimulationResult( have_been_scheduled=False, scheduler_acceptance_error_message= "Processors id must go from 0 to the number of" + " CPUS - 1", job_sections_execution={}, cpus_frequencies={}, scheduling_points=[], temperature_measures={}, hard_real_time_deadline_missed_stack_trace=None, memory_usage_record=None) # Check if scheduler is capable of execute task set can_schedule, error_message = scheduler.check_schedulability( processor_definition, environment_specification, tasks) if not can_schedule: return RawSimulationResult( have_been_scheduled=False, scheduler_acceptance_error_message="the scheduler can't schedule" if error_message is None else error_message, job_sections_execution={}, cpus_frequencies={}, scheduling_points=[], temperature_measures={}, hard_real_time_deadline_missed_stack_trace=None, memory_usage_record=None) # Run scheduler offline phase cpu_frequency = scheduler.offline_stage(processor_definition, environment_specification, tasks) # Create data structures for the simulation # Max frequency lcm_frequency = list_int_lcm(list(available_frequencies)) # Dict with activation and deadlines activation_dict, deadlines_dict = _create_deadline_arrive_dict( lcm_frequency, jobs) # Jobs CC dict by id (this value is constant and only should be used for fast access to the original cc) jobs_cc_dict: Dict[int, int] = {i.identifier: i.execution_time for i in jobs} # Remaining jobs CC dict by id remaining_cc_dict: Dict[int, int] = jobs_cc_dict.copy() # Simulation step actual_lcm_cycle: int = round(simulation_start_time * lcm_frequency) final_lcm_cycle: int = round(simulation_end_time * lcm_frequency) # Major cycle major_cycle_lcm = list_int_lcm( [round(i.period * lcm_frequency) for i in tasks.periodic_tasks]) # Jobs to task dict jobs_to_task_dict = {i.identifier: i.task.identifier for i in jobs} # Activate jobs set active_jobs = set() # Hard deadline task miss deadline hard_rt_task_miss_deadline = False # Only must take value if a hard real time is missed hard_real_time_deadline_missed_stack_trace: Optional[ SimulationStackTraceHardRTDeadlineMissed] = None # Jobs type dict hard_real_time_jobs: Set[int] = { i.identifier for i in jobs if i.task.deadline_criteria == Criticality.HARD } firm_real_time_jobs: Set[int] = { i.identifier for i in jobs if i.task.deadline_criteria == Criticality.FIRM } non_preemptive_jobs: Set[int] = { i.identifier for i in jobs if i.task.preemptive_execution == PreemptiveExecution.NON_PREEMPTIVE } # When is set the next scheduling point by quantum next_scheduling_point = None # Jobs being executed jobs_being_executed_id: Dict[int, int] = {} # Raw execution result tables job_sections_execution: Dict[int, List[JobSectionExecution]] = { i: [] for i in range(number_of_cpus) } # List of jobs executed by each core cpus_frequencies: Dict[int, List[CPUUsedFrequency]] = { i: [] for i in range(number_of_cpus) } # List of CPU frequencies used by each core scheduling_points: List[float] = [ ] # Points where the scheduler have made an scheduling temperature_measures: Dict[float, Dict[int, PhysicalCuboid]] = { } # Measures of temperature # Jobs being executed extra information [CPU, [start time]] jobs_last_section_start_time: Dict[int, float] = { i.identifier: -1 for i in jobs } jobs_last_cpu_used: Dict[int, int] = {i.identifier: -1 for i in jobs} jobs_last_preemption_remaining_cycles: Dict[int, int] = { i.identifier: -1 for i in jobs } # Last time frequency was set last_frequency_set_time = simulation_start_time # Board id board_thermal_id: int = number_of_cpus # Available memory jobs_memory_consumption: Dict[int, int] = { i.identifier: i.task.memory_footprint if i.task.memory_footprint is not None else 0 for i in jobs } memory_usage: int = 0 memory_usage_record: Dict[float, int] = {} # Energy management objects cubed_space: Optional[Model] = None initial_state: Optional[SimulationState] = None core_frequency_energy_activator: Optional[Dict[Tuple[int, int], int]] = None core_task_energy_activator: Optional[Dict[Tuple[int, int], int]] = None # Thermal options if simulation_options.simulate_thermal_behaviour: cubed_space, initial_state, core_frequency_energy_activator, core_task_energy_activator = _generate_cubed_space( tasks, processor_definition, environment_specification, simulation_options, board_thermal_id) # Main control loop while actual_lcm_cycle < final_lcm_cycle and not hard_rt_task_miss_deadline and \ len(active_jobs) + len(activation_dict) > 0: # Actual time in seconds actual_time_seconds = actual_lcm_cycle / lcm_frequency # Record temperature if simulation_options.simulate_thermal_behaviour: cubes_temperatures = cubed_space.obtain_temperature(initial_state) temperature_measures[actual_time_seconds] = cubes_temperatures cores_max_temperature = obtain_max_temperature(cubes_temperatures) cores_max_temperature.pop(board_thermal_id) # Record memory usage if simulation_options.simulate_memory_footprint: memory_usage_record[actual_time_seconds] = memory_usage # Major cycle start event major_cycle_event_require_scheduling = scheduler.on_major_cycle_start(actual_time_seconds) \ if actual_lcm_cycle % major_cycle_lcm == 0 else False # Job activation events activated_this_cycle = [(i, j) for i, j in activation_dict.items() if i <= actual_lcm_cycle] for i, j in activated_this_cycle: activation_dict.pop(i) for k in j: active_jobs.add(k) activation_event_require_scheduling_list = [ scheduler.on_jobs_activation(actual_time_seconds, i / lcm_frequency, [(k, jobs_to_task_dict[k]) for k in j]) for i, j in activated_this_cycle ] activation_event_require_scheduling = any( activation_event_require_scheduling_list) # Job end event jobs_that_have_end = [ i for i in active_jobs if remaining_cc_dict[i] == 0 ] for i in jobs_that_have_end: active_jobs.remove(i) # Update RawSimulationResult tables in case that a task end by cc # Remove it from executed tasks job_cpu_used = jobs_last_cpu_used[i] jobs_being_executed_id.pop(job_cpu_used) job_sections_execution[job_cpu_used].append((JobSectionExecution( i, jobs_to_task_dict[i], jobs_last_section_start_time[i], actual_time_seconds, jobs_last_preemption_remaining_cycles[i] - remaining_cc_dict[i]))) # Remove job from memory if simulation_options.simulate_memory_footprint: memory_usage = memory_usage - jobs_memory_consumption[i] end_event_require_scheduling = scheduler.on_job_execution_finished(actual_time_seconds, jobs_that_have_end) \ if len(jobs_that_have_end) > 0 else False # Job missed deadline events deadline_this_cycle = [(i, j) for i, j in deadlines_dict.items() if i <= actual_lcm_cycle] for i, _ in deadline_this_cycle: deadlines_dict.pop(i) jobs_deadline_this_cycle: List[int] = list( itertools.chain(*[j for _, j in deadline_this_cycle])) deadline_missed_this_cycle = [ i for i in jobs_deadline_this_cycle if i in active_jobs ] for i in (j for j in deadline_missed_this_cycle if j in firm_real_time_jobs): active_jobs.remove(i) # Remove firm real time from active set # Update RawSimulationResult tables in case that a task reach deadline and are firm job_cpu_used = jobs_last_cpu_used[i] if jobs_being_executed_id.__contains__( job_cpu_used ) and jobs_being_executed_id[job_cpu_used] == i: jobs_being_executed_id.pop(job_cpu_used) job_sections_execution[job_cpu_used].append( (JobSectionExecution( i, jobs_to_task_dict[i], jobs_last_section_start_time[i], actual_time_seconds, jobs_last_preemption_remaining_cycles[i] - remaining_cc_dict[i]))) # Remove job from memory if simulation_options.simulate_memory_footprint: memory_usage = memory_usage - jobs_memory_consumption[i] hard_rt_task_miss_deadline = any( (i in hard_real_time_jobs for i in deadline_missed_this_cycle )) # If some jab is hard real time set the flag deadline_missed_event_require_scheduling = False if hard_rt_task_miss_deadline: hard_real_time_deadline_missed_stack_trace = SimulationStackTraceHardRTDeadlineMissed( actual_time_seconds, { j: remaining_cc_dict[j] for j in deadline_missed_this_cycle if j in hard_real_time_jobs }) else: # Check if a deadline missed require rescheduling deadline_missed_event_require_scheduling = scheduler.on_jobs_deadline_missed(actual_time_seconds, deadline_missed_this_cycle) \ if len(deadline_missed_this_cycle) > 0 else False # Do scheduling if required if not hard_rt_task_miss_deadline and ( major_cycle_event_require_scheduling or activation_event_require_scheduling or end_event_require_scheduling or deadline_missed_event_require_scheduling or next_scheduling_point == actual_lcm_cycle) and len(active_jobs) > 0: # Call scheduler jobs_being_executed_id_next, cycles_until_next_scheduler_invocation, cores_frequency_next = \ scheduler.schedule_policy(actual_time_seconds, active_jobs, jobs_being_executed_id, cpu_frequency, None) if cores_frequency_next is None: cores_frequency_next = cpu_frequency # Scheduler result checks if simulation_options.scheduler_selections_check: bad_scheduler_behaviour = not ( available_frequencies.__contains__(cores_frequency_next) and all( (0 <= i < number_of_cpus for i in jobs_being_executed_id_next.keys())) and all( (i in active_jobs for i in jobs_being_executed_id_next.values())) and (cycles_until_next_scheduler_invocation is None or cycles_until_next_scheduler_invocation > 0)) if bad_scheduler_behaviour: exception_message = "Error due to bad scheduler behaviour\n" + \ "\t Jobs to CPU assignation: " + str(jobs_being_executed_id_next) + "\n" + \ "\t Active jobs: " + str(active_jobs) + "\n" + \ "\t Selected frequency: " + str(cores_frequency_next) + "\n" + \ "\t Available frequencies: " + \ str(available_frequencies) + "\n" + \ "\t Actual time: " + str(actual_time_seconds) raise Exception(exception_message) # Check if none preemptive task is preempted for i, j in jobs_being_executed_id.items(): if j in non_preemptive_jobs and remaining_cc_dict[j] > 0 and ( not jobs_being_executed_id_next.__contains__(i) or jobs_being_executed_id_next[i] != j): # If a non preemptive task have been preempted, its execution time must be restarted remaining_cc_dict[j] = jobs_cc_dict[j] # Check if a task is preempted for i, j in jobs_being_executed_id.items(): if not jobs_being_executed_id_next.__contains__( i) or jobs_being_executed_id_next[i] != j: job_sections_execution[i].append((JobSectionExecution( j, jobs_to_task_dict[j], jobs_last_section_start_time[j], actual_time_seconds, jobs_last_preemption_remaining_cycles[j] - remaining_cc_dict[j]))) # Check new tasks in execution for i, j in jobs_being_executed_id_next.items(): if not jobs_being_executed_id.__contains__( i) or jobs_being_executed_id[i] != j: jobs_last_preemption_remaining_cycles[ j] = remaining_cc_dict[j] jobs_last_section_start_time[j] = actual_time_seconds # Check if frequency have changed if cores_frequency_next != cpu_frequency: for i in range(number_of_cpus): cpus_frequencies[i].append( CPUUsedFrequency(cores_frequency_next, last_frequency_set_time, actual_time_seconds)) last_frequency_set_time = actual_time_seconds # Update memory usage if simulation_options.simulate_memory_footprint: memory_usage = memory_usage - sum(jobs_memory_consumption[i] for i in jobs_being_executed_id.values()) \ + sum(jobs_memory_consumption[i] for i in jobs_being_executed_id_next.values()) # Update RawSimulationResult tables scheduling_points.append(actual_time_seconds) # Update frequency and executed tasks cpu_frequency = cores_frequency_next jobs_being_executed_id = jobs_being_executed_id_next next_scheduling_point = ( (lcm_frequency // cpu_frequency) * cycles_until_next_scheduler_invocation + actual_lcm_cycle ) if cycles_until_next_scheduler_invocation is not None else None for i, j in jobs_being_executed_id.items(): jobs_last_cpu_used[j] = i # In case that it has been missed the state of the variables must keep without alteration if not hard_rt_task_miss_deadline: # Next cycle == min(keys(activation_dict), keys(deadline_dict), remaining cycles) next_major_cycle: int = major_cycle_lcm * ( (actual_lcm_cycle // major_cycle_lcm) + 1) next_job_end: int = min([ remaining_cc_dict[i] for i in jobs_being_executed_id.values() ]) * (lcm_frequency // cpu_frequency) + actual_lcm_cycle if len( jobs_being_executed_id) > 0 else next_major_cycle next_job_deadline: int = min(deadlines_dict.keys( )) if len(deadlines_dict) != 0 else next_major_cycle next_job_activation: int = min(activation_dict.keys( )) if len(activation_dict) != 0 else next_major_cycle next_lcm_cycle: int = min([ next_major_cycle, next_job_end, next_job_deadline, next_job_activation ] + ([next_scheduling_point] if next_scheduling_point is not None else [])) # This is just ceil((next_lcm_cycle - actual_lcm_cycle) / cpu_frequency) to advance an integer number # of cycles. # But with this formulation avoid floating point errors cc_to_advance = (((next_lcm_cycle - actual_lcm_cycle) // (lcm_frequency // cpu_frequency)) + (0 if (next_lcm_cycle - actual_lcm_cycle) % (lcm_frequency // cpu_frequency) == 0 else 1)) # Calculated update CC tables for i in jobs_being_executed_id.values(): remaining_cc_dict[i] -= cc_to_advance # Obtain temperature in the next simulation point if simulation_options.simulate_thermal_behaviour: if simulation_options.thermal_simulation_type == "DVFS": external_energy_point_execution = { core_frequency_energy_activator[(used_cpu, cpu_frequency)] for used_cpu in jobs_being_executed_id.keys() } elif simulation_options.thermal_simulation_type == "TASK_CONSUMPTION_MEASURED": external_energy_point_execution = { core_task_energy_activator[(used_cpu, task_executed)] for used_cpu, task_executed in jobs_being_executed_id.keys() } else: external_energy_point_execution = set() # Apply energy initial_state = cubed_space.apply_energy( actual_state=initial_state, amount_of_time=cc_to_advance / cpu_frequency, external_energy_application_points=Set.union( external_energy_point_execution, {i for i in range(number_of_cpus)}), internal_energy_application_points={ i for i in range(number_of_cpus) }) # Update actual_lcm_cycle actual_lcm_cycle += (lcm_frequency // cpu_frequency) * cc_to_advance # In the last cycle update RawSimulationResult tables (All jobs being executed) for i, j in jobs_being_executed_id.items(): job_sections_execution[i].append((JobSectionExecution( j, jobs_to_task_dict[j], jobs_last_section_start_time[j], actual_lcm_cycle / lcm_frequency, jobs_last_preemption_remaining_cycles[j] - remaining_cc_dict[j]))) # Record temperature if simulation_options.simulate_thermal_behaviour: cubes_temperatures = cubed_space.obtain_temperature(initial_state) temperature_measures[actual_lcm_cycle / lcm_frequency] = cubes_temperatures # In the last cycle update RawSimulationResult tables (Used frequencies) for i in range(number_of_cpus): cpus_frequencies[i].append( CPUUsedFrequency(cpu_frequency, last_frequency_set_time, simulation_end_time)) return RawSimulationResult( have_been_scheduled=True, scheduler_acceptance_error_message=None, job_sections_execution=job_sections_execution, cpus_frequencies=cpus_frequencies, scheduling_points=scheduling_points, temperature_measures=temperature_measures, hard_real_time_deadline_missed_stack_trace= hard_real_time_deadline_missed_stack_trace, memory_usage_record=memory_usage_record if simulation_options.simulate_memory_footprint else None)