From 07b693a186ca3047ed3f391cd49f290c180e1270 Mon Sep 17 00:00:00 2001 From: Zhantong Qiu Date: Fri, 2 Sep 2022 15:51:29 -0700 Subject: [PATCH] stdlib, configs: stdlib SimPoints support and example scripts simpoints-se-checkpoint.py & simpoints-se-restore.py: These are two example scripts to show how to use SimPoints functions with the stdlib. se_binary_workload.py: Allow se_binary_workload to take in SimPoint Class item and schedule SimPoint exit events. exit_event.py: Added SIMPOINT_BEGIN and MAX_INSTS exit events. simulator.py: Added SIMPOINT_BEGIN and MAX_INSTS exit event scheduling functions. They can schedule exit events before or during the simulation. Jira Issue: https://gem5.atlassian.net/browse/GEM5-1259 Change-Id: Iaa07a83de9dddc293b9f1a230aba8e35d4f5af6c Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/63154 Reviewed-by: Bobby Bruce Tested-by: kokoro Maintainer: Bobby Bruce --- .../checkpoints/simpoints-se-checkpoint.py | 124 +++++++++++++++ .../checkpoints/simpoints-se-restore.py | 147 ++++++++++++++++++ .../components/boards/se_binary_workload.py | 15 ++ src/python/gem5/simulate/exit_event.py | 6 + src/python/gem5/simulate/simulator.py | 37 +++++ 5 files changed, 329 insertions(+) create mode 100644 configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py create mode 100644 configs/example/gem5_library/checkpoints/simpoints-se-restore.py diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py new file mode 100644 index 0000000000..6094f09de1 --- /dev/null +++ b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py @@ -0,0 +1,124 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This configuation script shows an example of how to take checkpoints for +SimPoints using the gem5 stdlib. SimPoints, SimPoints interval length, +SimPoints weights, and the warmup instruction length are passed into the gem5 +SimPoint module. The gem5 SimPoint module will calculate where to take +checkpoints based of the SimPoints, SimPoints interval length, and the warmup +instruction length. In SE mode, when you pass in a SimPoint object to the +set_se_binary_workload, it will schedule exit events for SimPoints in the init +stage of the core. With the Simulator module and the exit event generator, +checkpoints will be taken for the SimPoints. + +This scipt builds a simple board with the gem5 stdlib with no cache and a +simple memory structure to take checkpoints. Some of the components, such as +cache hierarchy, can be changed when restoring checkpoints. + +Usage +----- + +``` +scons build/X86/gem5.opt +./build/X86/gem5.opt \ + configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py + +./build/X86/gem5.opt \ + configs/example/gem5_library/checkpoints/simpoints-se-restore.py +``` +""" + +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from gem5.resources.resource import Resource +from pathlib import Path +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.utils.simpoint import SimPoint +from gem5.simulate.exit_event_generators import ( + save_checkpoint_generator, +) + +requires(isa_required=ISA.X86) + +# When taking a checkpoint, the cache state is not saved, so the cache +# hierarchy can be changed completely when restoring from a checkpoint. +# By using NoCache() to take checkpoints, it can slightly improve the +# performance when running in atomic mode, and it will not put any restrictions +# on what people can do with the checkpoints. +cache_hierarchy = NoCache() + +# Using simple memory to take checkpoints might slightly imporve the +# performance in atomic mode. The memory structure can be changed when +# restoring from a checkpoint, but the size of the memory must be maintained. +memory = SingleChannelDDR3_1600(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, + isa=ISA.X86, + # SimPoints only works with one core + num_cores=1, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +simpoint = SimPoint( + simpoint_list=[2, 3, 5, 15], + weight_list=[0.1, 0.2, 0.4, 0.3], + simpoint_interval=1000000, + warmup_interval=1000000, +) + +board.set_se_binary_workload( + binary=Resource("x86-print-this"), + arguments=["print this", 15000], + simpoint=simpoint, +) + +dir = Path("se_checkpoint_folder/") +dir.mkdir(exist_ok=True) + +simulator = Simulator( + board=board, + on_exit_event={ + # using the SimPoints event generator in the standard library to take + # checkpoints + ExitEvent.SIMPOINT_BEGIN: save_checkpoint_generator(dir) + }, +) + +simulator.run() diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-restore.py b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py new file mode 100644 index 0000000000..1a1bb05496 --- /dev/null +++ b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py @@ -0,0 +1,147 @@ +# Copyright (c) 2022 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This configuation script shows an example of how to restore a checkpoint that +was taken for SimPoints in the +configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py. +The SimPoints, SimPoints interval length, and the warmup instruction length +are passed into the SimPoint module, so the SimPoint object will store and +calculate the warmup instruction length for each SimPoints based on the +avaliable instructions before reaching the start of the SimPoint. With the +Simulator module, exit event will be generated to stop when the warmup session +ends and the SimPoints interval ends. + +This scipt builds a more complex board than the board used for taking +checkpoint. + +Usage +----- + +``` +scons build/X86/gem5.opt +./build/X86/gem5.opt \ + configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py + +./build/X86/gem5.opt \ + configs/example/gem5_library/checkpoints/simpoints-se-restore.py +``` +""" + +import imp +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from gem5.resources.resource import Resource + +from gem5.utils.simpoint import SimPoint +from pathlib import Path +from m5.stats import reset, dump + +requires(isa_required=ISA.X86) + +# The cache hierarchy can be different from the cache hierarchy used in taking +# the checkpoints +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="32kB", + l1i_size="32kB", + l2_size="256kB", +) + +# The memory structure can be different from the memory structure used in +# taking the checkpoints, but the size of the memory must be maintained +memory = DualChannelDDR4_2400(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, + isa=ISA.X86, + num_cores=1, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +simpoint = SimPoint( + simpoint_list=[2, 3, 5, 15], + weight_list=[0.1, 0.2, 0.4, 0.3], + simpoint_interval=1000000, + warmup_interval=1000000, +) + +board.set_se_binary_workload( + binary=Resource("x86-print-this"), arguments=["print this", 15000] +) + +# configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py has to +# run before running this script. +# In here, it will get the path of the first SimPoint checkpoint taken by the +# simpoints-se-checkpoint.py +dir = Path("se_checkpoint_folder") +subfolder = [int(str(name).split(".")[1]) for name in dir.iterdir()] +dir = Path(dir / f"cpt.{min(subfolder)}").as_posix() + + +def max_inst(): + warmed_up = False + while True: + if warmed_up: + print("end of SimPoint interval") + yield True + else: + print("end of warmup, starting to simulate SimPoint") + warmed_up = True + # Schedule a MAX_INSTS exit event during the simulation + simulator.schedule_max_insts(simpoint.get_simpoint_interval()) + dump() + reset() + yield False + + +simulator = Simulator( + board=board, + checkpoint_path=dir, + on_exit_event={ExitEvent.MAX_INSTS: max_inst()}, +) + +# Schedule a MAX_INSTS exit event before the simulation begins the +# schedule_max_insts function only schedule event when the instruction length +# is greater than 0. +# In here, it schedules an exit event for the first SimPoint's warmup +# instructions +simulator.schedule_max_insts(simpoint.get_warmup_list()[0], True) +simulator.run() diff --git a/src/python/gem5/components/boards/se_binary_workload.py b/src/python/gem5/components/boards/se_binary_workload.py index 76865cd949..62837fd848 100644 --- a/src/python/gem5/components/boards/se_binary_workload.py +++ b/src/python/gem5/components/boards/se_binary_workload.py @@ -26,10 +26,12 @@ from .abstract_board import AbstractBoard from ...resources.resource import AbstractResource +from gem5.utils.simpoint import SimPoint from m5.objects import SEWorkload, Process from typing import Optional, List +from m5.util import warn class SEBinaryWorkload: @@ -52,6 +54,7 @@ class SEBinaryWorkload: exit_on_work_items: bool = True, stdin_file: Optional[AbstractResource] = None, arguments: List[str] = [], + simpoint: SimPoint = None, ) -> None: """Set up the system to run a specific binary. @@ -60,11 +63,16 @@ class SEBinaryWorkload: * Dynamically linked executables are partially supported when the host ISA and the simulated ISA are the same. + **Warning:** SimPoints only works with one core + :param binary: The resource encapsulating the binary to be run. :param exit_on_work_items: Whether the simulation should exit on work items. True by default. :param stdin_file: The input file for the binary :param arguments: The input arguments for the binary + :param simpoint: The SimPoint object that contains the list of + SimPoints starting instructions, the list of weights, and the SimPoints + interval """ # We assume this this is in a multiple-inheritance setup with an @@ -87,5 +95,12 @@ class SEBinaryWorkload: for core in self.get_processor().get_cores(): core.set_workload(process) + if simpoint is not None: + if self.get_processor().get_num_cores() > 1: + warn("SimPoints only works with one core") + self.get_processor().get_cores()[0].set_simpoint( + inst_starts=simpoint.get_simpoint_start_insts(), init=True + ) + # Set whether to exit on work items for the se_workload self.exit_on_work_items = exit_on_work_items diff --git a/src/python/gem5/simulate/exit_event.py b/src/python/gem5/simulate/exit_event.py index 294ff001b5..691e41a8a1 100644 --- a/src/python/gem5/simulate/exit_event.py +++ b/src/python/gem5/simulate/exit_event.py @@ -46,6 +46,8 @@ class ExitEvent(Enum): USER_INTERRUPT = ( # An exit due to a user interrupt (e.g., cntr + c) "user interupt" ) + SIMPOINT_BEGIN = "simpoint begins" + MAX_INSTS = "number of instructions reached" @classmethod def translate_exit_status(cls, exit_string: str) -> "ExitEvent": @@ -81,6 +83,10 @@ class ExitEvent(Enum): return ExitEvent.CHECKPOINT elif exit_string == "user interrupt received": return ExitEvent.USER_INTERRUPT + elif exit_string == "simpoint starting point found": + return ExitEvent.SIMPOINT_BEGIN + elif exit_string == "a thread reached the max instruction count": + return ExitEvent.MAX_INSTS raise NotImplementedError( "Exit event '{}' not implemented".format(exit_string) ) diff --git a/src/python/gem5/simulate/simulator.py b/src/python/gem5/simulate/simulator.py index b6474a41d6..efa7c421e5 100644 --- a/src/python/gem5/simulate/simulator.py +++ b/src/python/gem5/simulate/simulator.py @@ -41,6 +41,7 @@ from .exit_event_generators import ( default_switch_generator, default_workbegin_generator, default_workend_generator, + default_simpoint_generator, ) from .exit_event import ExitEvent from ..components.boards.abstract_board import AbstractBoard @@ -179,6 +180,8 @@ class Simulator: ExitEvent.WORKEND: default_workend_generator(), ExitEvent.USER_INTERRUPT: default_exit_generator(), ExitEvent.MAX_TICK: default_exit_generator(), + ExitEvent.SIMPOINT_BEGIN: default_simpoint_generator(), + ExitEvent.MAX_INSTS: default_simpoint_generator(), } if on_exit_event: @@ -197,6 +200,40 @@ class Simulator: self._checkpoint_path = checkpoint_path + def schedule_simpoint( + self, simpoint_start_insts: List[int], schedule_at_init: bool = False + ) -> None: + """ + Schedule SIMPOINT_BEGIN exit events + + **Warning:** SimPoints only work with one core + + :param simpoint_start_insts: a list of number of instructions + indicating the starting point of the simpoints + :param schedule_at_init: if it is True, schedule the events in the init + stage of the core, else, schedule the events during the simulation + """ + if self._board.get_processor().get_num_cores() > 1: + warn("SimPoints only work with one core") + self._board.get_processor().get_cores()[0].set_simpoint( + simpoint_start_insts, schedule_at_init + ) + + def schedule_max_insts( + self, inst: int, schedule_at_init: bool = False + ) -> None: + """ + Schedule a MAX_INSTS exit event when any thread in the current core + reaches the given number of instructions + + :param insts: a number of instructions + :param schedule_at_init: if it is True, schedule the event in the init + stage of the core, else, schedule the event during the simulation + """ + self._board.get_processor().get_cores()[0].set_inst_stop_any_thread( + inst, schedule_at_init + ) + def get_stats(self) -> Dict: """ Obtain the current simulation statistics as a Dictionary, conforming