learning_gem5 part2_08 ARM DVFS 建模

与大多数现代 CPU 一样,ARM CPU 支持 DVFS(动态电压频率调整)。可以在 gem5 中对此进行建模,并监控由此产生的功耗使用情况。DVFS 建模是通过使用时钟对象的两个组件来实现的:电压域和时钟域。本章节详细介绍了这些不同的组件,并展示了将它们添加到现有模拟中的不同方法。

电压域

电压域规定了 CPU 可以使用的电压值。如果在 gem5 中运行全系统模拟时没有指定电压域,则使用默认值 1.0 伏特。这是为了避免在用户对模拟电压不感兴趣时强迫他们考虑电压问题。

电压域可以通过单个值或值列表来构造,通过 voltage 关键字参数传递给 VoltageDomain 构造函数。如果指定了单个值和多个频率,则该电压将用于时钟域中的所有频率。如果指定了电压值列表,则其条目数必须与相应时钟域中的条目数匹配,并且条目必须按降序排列。与真实硬件一样,一个电压域适用于整个处理器插槽。这意味着如果您希望为不同的处理器(例如,在 big.LITTLE 设置中)设置不同的电压域,您需要确保 big 集群和 LITTLE 集群在不同的插槽上(检查与集群关联的 socket_id 值)。

有两种方法可以将电压域添加到现有的 CPU/模拟中,一种更灵活,另一种更直接。第一种方法向提供的 configs/example/arm/fs_bigLITTLE.py 文件添加命令行标志,而第二种方法添加自定义类。

  1. 使用命令行标志(更灵活)

    向模拟添加电压域最灵活的方法是使用命令行标志。要添加命令行标志,请在文件的 addOptions 函数中找到并添加该标志,可以选择附带一些帮助文本。

    支持单个和多个电压的示例:

    python 复制代码
    def addOptions(parser):
        [...]
        parser.add_argument("--big-cpu-voltage", nargs="+", default="1.0V",
                            help="Big CPU voltage(s).")
        return parser

    然后可以使用以下方式指定电压域值:

    复制代码
    --big-cpu-voltage <val1>V [<val2>V [<val3>V [...]]]

    这将在 build 函数中通过 options.big_cpu_voltage 访问。nargs="+" 确保至少需要一个参数。在 build 中的使用示例:

    python 复制代码
    def build(options):
        [...]
        # big cluster
        if options.big_cpus > 0:
            system.bigCluster = big_model(system, options.big_cpus,
                                          options.big_cpu_clock,
                                          options.big_cpu_voltage)
        [...]

    可以添加类似的标志和对 build 函数的补充,以支持为 LITTLE CPU 指定电压值。这种方法可以非常容易地指定和修改电压。这种方法的唯一缺点是多个命令行参数(有些是列表形式)可能会使用于调用模拟器的命令变得混乱。

  2. 使用子类(不太灵活)

    指定电压域的不太灵活的方法是创建 CpuCluster 的子类。与现有的 BigClusterLittleCluster 子类类似,这些子类将扩展 CpuCluster 类。在子类的构造函数中,除了指定 CPU 类型外,我们还为电压域定义一个值列表,并使用 cpu_voltage 关键字参数将其传递给父类构造函数。以下是为 BigCluster 添加电压的示例:

    python 复制代码
    class VDBigCluster(devices.CpuCluster):
        def __init__(self, system, num_cpus, cpu_clock=None, cpu_voltage=None):
            # use the same CPU as the stock BigCluster
            abstract_cpu = ObjectList.cpu_list.get("O3_ARM_v7a_3")
            # voltage value(s)
            my_voltages = [ '1.0V', '0.75V', '0.51V']
    
            super(VDBigCluster, self).__init__(
                cpu_voltage=my_voltages,
                system=system,
                num_cpus=num_cpus,
                cpu_type=abstract_cpu,
                l1i_type=devices.L1I,
                l1d_type=devices.L1D,
                wcache_type=devices.WalkCache,
                l2_type=devices.L2
            )

    然后可以通过定义类似的 VDLittleCluster 类来为 LittleCluster 添加电压。

    定义了子类之后,我们仍然需要向文件中的 cpu_types 字典添加一个条目,指定一个字符串名称作为键,一对类作为值,例如:

    python 复制代码
    cpu_types = {
        [...]
        "vd-timing" : (VDBigCluster, VDLittleCluster)
    }

    然后可以通过传递以下命令来使用具有电压域的 CPU:

    复制代码
    --cpu-type vd-timing

    由于对电压值的任何修改都必须通过找到正确的子类并修改其代码,或者添加更多子类和 cpu_types 条目来完成,因此这种方法比基于标志的方法灵活性差很多。

时钟域

电压域与时钟域结合使用。如前所述,如果未指定自定义电压值,则对时钟域中的所有值使用默认值 1.0V。

时钟域的类型

与电压域相反,时钟域有 3 种类型(来自 src/sim/clock_domain.hh):

  • ClockDomain -- 为捆绑在同一时钟域下的一组时钟对象提供时钟。时钟域又分组到电压域中。时钟域支持具有"源"和"派生"时钟域的层次结构。

  • SrcClockDomain -- 提供了连接到可调时钟源的时钟域的概念。它维护时钟周期并提供设置/获取时钟的方法,以及处理程序将要管理的时钟域的配置参数。这包括不同性能级别下的频率值、域 ID 和当前性能级别。请注意,软件请求的性能级别对应于时钟域可以运行的频率操作点之一。

  • DerivedClockDomain -- 提供了连接到父时钟域的时钟域的概念,父时钟域可以是 SrcClockDomainDerivedClockDomain。它维护时钟分频器并提供获取时钟的方法。

向现有模拟添加时钟域

此示例将使用与电压域示例相同的提供文件,即 configs/example/arm/fs_bigLITTLE.pyconfigs/example/arm/devices.py

与电压域类似,时钟域可以是单个值或值列表。如果给定了时钟速度列表,则适用与提供给电压域的电压列表相同的规则,即时钟域中的值数量必须与电压域中的值数量匹配;并且时钟速度必须按降序给出。提供的文件支持将时钟指定为单个值(通过 --{big,little}-cpu-clock 标志),但不支持作为值列表。扩展/修改所提供标志的行为是添加多值时钟域支持的最简单和最灵活的方法,但也可以通过添加子类来实现。

  1. 为现有的 --{big,little}-cpu-clock 标志添加多值支持

    找到 configs/example/arm/fs_bigLITTLE.py 文件中的 addOptions 函数。在各种 parser.add_argument 调用中,找到添加 CPU 时钟标志的那些,并将关键字参数 type=str 替换为 nargs="+"

    python 复制代码
    def addOptions(parser):
        [...]
        parser.add_argument("--big-cpu-clock", nargs="+", default="2GHz",
                            help="Big CPU clock frequency.")
        parser.add_argument("--little-cpu-clock", nargs="+", default="1GHz",
                            help="Little CPU clock frequency.")
        [...]

    这样,可以类似于用于电压域的标志来指定多个频率:

    复制代码
    --{big,little}-cpu-clock <val1>GHz [<val2>MHz [<val3>MHz [...]]]

    由于这修改了现有的标志,标志的值已经连接到 build 函数中的相关构造函数和关键字参数,因此在那里不需要修改任何东西。

  2. 在子类中添加时钟域

    这个过程与将电压域作为子类添加的过程非常相似。区别在于,我们不是指定电压并使用 cpu_voltage 关键字参数,而是指定时钟值并在 super 调用中使用 cpu_clock 关键字参数:

    python 复制代码
    class CDBigCluster(devices.CpuCluster):
        def __init__(self, system, num_cpus, cpu_clock=None, cpu_voltage=None):
            # use the same CPU as the stock BigCluster
            abstract_cpu = ObjectList.cpu_list.get("O3_ARM_v7a_3")
            # clock value(s)
            my_freqs = [ '1510MHz', '1000MHz', '667MHz']
    
            super(CDBigCluster, self).__init__( # 注意:这里类名修正为 CDBigCluster
                cpu_clock=my_freqs,
                system=system,
                num_cpus=num_cpus,
                cpu_type=abstract_cpu,
                l1i_type=devices.L1I,
                l1d_type=devices.L1D,
                wcache_type=devices.WalkCache,
                l2_type=devices.L2
            )

    这可以与电压域示例结合,以便为集群同时指定电压域和时钟域。

    与使用此方法添加电压域一样,您需要为您想要使用的每种 CPU 类型定义一个类,并在 cpu_types 字典中指定它们的名称-cpuPair 值。这种方法也有相同的局限性,并且比基于标志的方法灵活性差很多。

确保时钟域具有有效的域 ID

无论使用前面的哪种方法,都需要进行一些额外的修改。这些修改涉及提供的 configs/example/arm/devices.py 文件。

在文件中,找到 CpuClusters 类,并找到 self.clk_domain 被初始化为 SrcClockDomain 的地方。如上文关于 SrcClockDomain 的注释所述,这些域有一个域 ID。如果没有设置,就像在提供的设置中那样,将使用默认 ID -1。请更改代码以确保设置了域 ID:

python 复制代码
[...]
self.clk_domain = SrcClockDomain(clock=cpu_clock,
                                 voltage_domain=self.voltage_domain,
                                 domain_id=system.numCpuClusters())
[...]

这里使用 system.numCpuClusters() 是因为时钟域适用于整个集群,即第一个集群为 0,第二个集群为 1,依此类推。

如果您不设置域 ID,当尝试运行支持 DVFS 的模拟时,您将遇到以下错误,因为一些内部检查捕获了默认的域 ID:

复制代码
fatal: fatal condition domain_id == SrcClockDomain::emptyDomainID occurred:
DVFS: Controlled domain system.bigCluster.clk_domain needs to have a properly
assigned ID.

DVFS 处理程序

如果您指定了电压域和时钟域,然后尝试运行模拟,它很可能会运行,但您可能会在输出中注意到以下警告:

复制代码
warn: Existing EnergyCtrl, but no enabled DVFSHandler found.

电压域和时钟域已添加,但没有系统可以与之交互以调整值的 DVFSHandler。解决此问题的最简单方法是在 configs/example/arm/fs_bigLITTLE.py 文件中添加另一个命令行标志。

与电压域和时钟域示例一样,找到 addOptions 函数并将以下代码附加到其中:

python 复制代码
def addOptions(parser):
    [...]
    parser.add_argument("--dvfs", action="store_true",
                        help="Enable the DVFS Handler.")
    return parser

然后,找到 build 函数并将此代码附加到其中:

python 复制代码
def build(options):
    [...]
    if options.dvfs:
        system.dvfs_handler.domains = [system.bigCluster.clk_domain,
                                       system.littleCluster.clk_domain]
        system.dvfs_handler.enable = options.dvfs

    return root

完成这些设置后,您现在应该能够通过在调用模拟时使用 --dvfs 标志来运行支持 DVFS 的模拟,并可以根据需要指定 big 和 LITTLE 集群的电压和频率操作点。

相关推荐
Eloudy2 天前
一问理解 gem5 与 archmodel 和 cmodel 的关系
gpu·arch·gem5
Seal软件5 天前
GPUStack v2:推理加速释放算力潜能,开源重塑大模型推理下半场
llm·gpu
Eloudy6 天前
learning_gem5 part1_05 gem5 v24.1:使用 gem5 标准库配置脚本
gpu·arch·gem5
Eloudy6 天前
gem5 的统计包 和 API
arch·gem5
Eloudy8 天前
learning_gem5 part1_04 理解gem5统计信息与输出文件
gpu·arch·gem5
Eloudy9 天前
learning_gem5 part1_02 创建简单的配置脚本
gem5
Eloudy9 天前
全文 -- GPU-Initiated Networking for NCCL
gpu·arch
HyperAI超神经9 天前
【TVM 教程】优化大语言模型
人工智能·语言模型·自然语言处理·cpu·gpu·编程语言·tvm
Eloudy9 天前
learning_gem5 part1_01 构建 gem5
gem5