开发 gem5 模型

使用 Garnet 建模片上网络


Ruby 回顾

Inside Ruby: controllers around an interconnect model cloud bg right fit


片上网络

拓扑互连模型组成

拓扑

在 Python 配置中指定路由器/交换机如何连接

互连

首先我们将创建一个新的拓扑(环形),然后扩展它以使用 Garnet。


创建新拓扑

来自缓存一致性建模 每个控制器和路由器之间需要一个外部链路

self.routers = [
    Switch(router_id = i)
    for i in range(len(controllers))
]
self.ext_links = [
    SimpleExtLink(
        link_id=i, ext_node=c,
        int_node=self.routers[i])
    for i, c in enumerate(controllers)
]

diagram showing cache/directory controllers connected to routers  bg right fit


创建新拓扑

来自缓存一致性建模 在路由器之间创建内部链路。

for ri in self.routers:
    for rj in self.routers:
        # 不要将路由器连接到自身!
        if ri == rj: continue
        link_count += 1
        self.int_links.append(
            SimpleIntLink(
                link_id = link_count,
                src_node=ri, dst_node=rj
            )
        )

diagram showing cache/directory controllers connected to routers and routers connected with links bg right fit


让我们为 CHI 协议创建一个环形拓扑

基于CHI 协议构建

diagram showing a ring topology with four cores, two memory controllers and two L2s


创建拓扑文件

打开 https://github.com/gem5bootcamp/2024/blob/main/materials/03-Developing-gem5-models/08-ruby-network/ring.py

注意:这段代码中有很多奇怪的地方。大部分内容,你只需要相信我的话…


扩展 SimpleNetwork

class Ring(SimpleNetwork):
    def __init__(self, ruby_system):
        super().__init__()
        self.netifs = []
        self.ruby_system = ruby_system

注意 netifs 仅用于 Garnet。此外,必须手动设置 ruby_system


connectControllers 方法

这是创建拓扑核心内容的地方。 在我们的例子中,我们将使这个拓扑非常具体。

重要提示:拓扑的布局与核心数量、L2 缓存组、内存控制器等密切相关。

例如,考虑一下如何布局网格拓扑。

def connectControllers(
    self, l1i_ctrls, l1d_ctrls, l2_ctrls, mem_ctrls, dma_ctrls
):
    assert len(l1i_ctrls) == 4
    assert len(l1d_ctrls) == 4
    assert len(l2_ctrls) == 2
    assert len(mem_ctrls) == 2

为 L1D 和 L1I 缓存创建路由器

L1I 和 L1D 可以共享同一个路由器。L2 缓存有自己独立的路由器。

self.l1_routers = [Switch(router_id=i) for i in range(4)]
self.l1i_ext_links = [
    SimpleExtLink(link_id=i, ext_node=c, int_node=self.l1_routers[i])
    for i, c in enumerate(l1i_ctrls)
]
self.l1d_ext_links = [
    SimpleExtLink(link_id=4+i, ext_node=c, int_node=self.l1_routers[i])
    for i, c in enumerate(l1d_ctrls)
]

注意:link_id 很重要。你必须手动递增它,并确保每种类型的 id 都是唯一的。 就像很多事情一样,可能有更好的方法,但这就是它的实现方式…


为 L2 缓存和内存创建路由器

self.l2_routers = [Switch(router_id=4+i) for i in range(2)]
self.l2_ext_links = [
    SimpleExtLink(link_id=8+i, ext_node=c, int_node=self.l2_routers[i])
    for i, c in enumerate(l2_ctrls)
]

self.mem_routers = [Switch(router_id=6+i) for i in range(2)]
self.mem_ext_links = [
    SimpleExtLink(link_id=10+i, ext_node=c, int_node=self.mem_routers[i])
    for i, c in enumerate(mem_ctrls)
]

不要忘记链路 id!


最后,如果我们在 FS 模式下运行,需要 DMA

if dma_ctrls:
    self.dma_ext_links = [
        SimpleExtLink(
            link_id=12+i, ext_node=c, int_node=self.mem_routers[0]
        )
        for i, c in enumerate(dma_ctrls)
    ]

创建内部链路

这是我们创建环形拓扑的地方。 为了有所不同,让我们做一个单向环形。

self.int_links = [
    SimpleIntLink(
        link_id=0,
        src_node=self.l1_routers[0],
        dst_node=self.l1_routers[1],
    ),
    SimpleIntLink(
        link_id=1,
        src_node=self.l1_routers[1],
        dst_node=self.mem_routers[0],
    ),
...

diagram showing a ring topology with four cores, two memory controllers and two L2s bg right fit


更多样板代码

我们必须告诉父网络类我们的链路和路由器。它需要成员变量 routersext_linksint_links

self.ext_links = (
    self.l1i_ext_links
    + self.l1d_ext_links
    + self.l2_ext_links
    + self.mem_ext_links
    + getattr(self, "dma_ext_links", [])
)
self.routers = (
    self.l1_routers
    + self.l2_routers
    + self.mem_routers
)

测试结果

gem5 run-test.py
board.processor.cores0.generator.readBW  2095164513.646249
board.processor.cores1.generator.readBW  2219964305.979394
board.processor.cores2.generator.readBW  2057532576.265793
board.processor.cores3.generator.readBW  2124156465.403641

注意:这比我们看到的点对点连接低近 10%!


想法!交换 L2 和内存的位置

    dst_node=self.l2_routers[0],
),
SimpleIntLink(
    link_id=2,
    src_node=self.l2_routers[0],
    dst_node=self.mem_routers[0],
),
SimpleIntLink(
    link_id=3,
    src_node=self.mem_routers[0],
    dst_node=self.l1_routers[2],
SimpleIntLink(
    link_id=5,
    src_node=self.l1_routers[3],
    dst_node=self.l2_routers[1],
),
SimpleIntLink(
    link_id=6,
    src_node=self.l2_routers[1],
    dst_node=self.mem_routers[1],
),
SimpleIntLink(
    link_id=7,
    src_node=self.mem_routers[1],

再次运行

gem5 run-test.py
board.processor.cores0.generator.readBW  2370664319.434113
board.processor.cores1.generator.readBW  2472807299.127889
board.processor.cores2.generator.readBW  2414887877.684990
board.processor.cores3.generator.readBW  2504614981.400951

Garnet


Simple vs Garnet

路由器微架构

链路微架构


路由


Garnet 扩展


示例?

… 不幸的是,这不起作用。但我有一个变通方法…

肯定有某个地方的缓冲区正在填满,导致死锁。

问题是我不能确定是在网络、协议还是其他地方。

使用 Ruby 和 Garnet 时就是这样!


Garnet 的更改

  1. 复制 ring.py
cp ring.py ring_garnet.py
  1. 更改 hierarchy.py 以使用 ring_garnet,并删除以下行
-     self.ruby_system.network.setup_buffers()
  1. ring_garnet.py 中进行以下替换

还有一个更改(以及一个变通方法)

还要在 Ring.connectControllers 中添加以下内容

self.netifs = [GarnetNetworkInterface(id=i) \
            for (i,n) in enumerate(self.ext_links)]

在构造函数中添加以下内容以绕过死锁。

# 如果我必须这样做才能让它工作,肯定有什么地方出了问题。
self.ni_flit_size = 64
self.vcs_per_vnet = 16

现在,再次运行测试!

gem5 run-test.py
board.processor.cores0.generator.readBW  3248115023.780479
board.processor.cores1.generator.readBW  3149747416.759070
board.processor.cores2.generator.readBW  3317362747.135825
board.processor.cores3.generator.readBW  3113523561.473372

注意:使用 Garnet 进行模拟比使用 SimpleNetwork 需要更长的时间。 更高的保真度意味着更长的模拟时间!