L1 数据缓存标签包含基于线性地址的微标签 (utag)
用线性地址标记每个缓存行
最初访问缓存行。
L1D 中的每个缓存行都有一个与之关联的 utag。这意味着 utag 内存结构的组织方式与 L1D 完全相同(即 8 路和 64 组),并且条目之间存在一对一的对应关系。 utag 是根据导致在 L1D 中填充该行的请求的线性地址计算得出的。
加载使用此 utag 来确定使用哪种方式读取缓存
它们的线性地址,在负载的物理地址之前可用
地址已通过 TLB 确定。
加载的线性地址同时发送到路预测器和TLB(最好使用术语MMU,因为有多个TLB)。使用线性地址 (11:6) 的某些位选择 utag 内存中的特定集合,同时读取该集合中的所有 8 个 utag。同时,根据加载请求的线性地址计算出一个utag。当这两个操作都完成时,将给定的 utag 与存储在集合中的所有 utag 进行比较。保留 utag 内存,以便在每个集合中最多可以有一个具有相同值的 utag。在 utag 内存中发生命中的情况下,预测器预测目标缓存行在 L1D 中的相应缓存条目中的方式。到目前为止,还不需要物理地址。
utag 是加载的线性地址的散列。
哈希函数在第 3 节中标题为 Take A Way: Exploring the Security Implications of AMD’s Cache Way Predictors 的论文中针对许多微架构进行了逆向工程。基本上,位置 27:12 的线性地址的某些位相互异或以产生一个 8 位值,即 utag。一个好的哈希函数应该:(1) 最小化映射到相同 utag 的线性地址对的数量,(2) 最小化 utag 的大小,以及 (3) 具有不大于 utag 内存访问延迟的延迟。 /p>
这种基于线性地址的查找可以非常准确地预测
缓存线在读取缓存之前以何种方式定位
数据。这允许负载仅读取单个缓存方式,而不是
全部 8. 这样可以节省电力并减少银行冲突。
除了 utag 内存和相关逻辑,L1D 还包括一个标签内存和一个数据内存,它们都具有相同的组织结构。标签存储器存储物理标签(第 6 位到物理地址的最高位)。数据存储器存储高速缓存行。在utag命中的情况下,方式预测器仅以相应方式读取标签内存和数据内存中的一个条目。在现代 x86 处理器上,物理地址的大小超过 35 位,因此物理标签的大小超过 29 位。这比 utag 的大小大 3 倍以上。如果没有路预测,在具有多个缓存路的缓存中,必须并行读取和比较多个标签。在 8 路缓存中,读取和比较 1 个标签比读取和比较 8 个标签消耗的能量要少得多。
在可以单独激活每种方式的缓存中,每个缓存条目都有自己的字线,与跨多个缓存方式共享的世界线相比,它更短。由于信号传播延迟,读取单路比读取 8 路花费的时间更少。然而,在并行访问的缓存中,没有办法预测延迟,但线性地址转换成为加载延迟的关键路径。通过方式预测,来自预测条目的数据可以推测性地转发到相关的微指令。这可以提供显着的加载延迟优势,特别是因为线性地址转换延迟可能会因 MMU 的多级设计而变化,即使在 MMU 命中的典型情况下也是如此。缺点是它引入了一个可能发生重放的新原因:如果发生错误预测,可能需要重放数十甚至数百个微指令。我不知道 AMD 是否真的在验证预测之前转发了请求的数据,但即使手册中没有提到它也是可能的。
减少银行冲突是手册中提到的方式预测的另一个优点。这意味着不同的方式被放置在不同的银行中。第 2.6.2.1 节说地址的 5:2 位、访问的大小和高速缓存路数决定了要访问的存储体。这表明有 16*8 = 128 个存储库,每个存储库对应每个 4 字节块。位 5:2 是从负载的线性地址中获得的,负载的大小是从负载 uop 中获得的,路数是从路预测器中获得的。第 2.6.2 节说 L1D 在同一周期内支持两个 16 字节加载和一个 16 字节存储。这表明每个存储库都有一个 16 字节的读写端口。 128 个组端口中的每一个都通过互连连接到 L1D 数据存储器的 3 个端口中的每一个。 3 个端口中的一个连接到存储缓冲区,另外两个连接到加载缓冲区,可能带有用于有效处理跨线加载的中间逻辑(单个加载 uop,但结果合并的两个加载请求),重叠加载(以避免银行冲突),以及跨越银行边界的负载。
路径预测只需要访问标签存储器和 L1D 数据存储器中的单一路径这一事实允许减少或完全消除使标签和数据存储器真正多端口的需要(取决于如何处理窥探)(这是英特尔在 Haswell 中采用的方法),同时仍实现了大致相同的吞吐量。但是,当同时访问相同的方式和相同的 5:2 地址位,但 utag 不同时,仍然可能发生存储区冲突。方式预测确实减少了银行冲突,因为它不需要为每次访问读取多个条目(至少在标签内存中,但也可能在数据内存中),但它并不能完全消除银行冲突。
也就是说,标签内存可能需要真正的多端口来处理填充检查(见下文)、验证检查(见下文)、窥探和非加载访问的“正常路径”检查。我认为只有加载请求使用预测器的方式。其他类型的请求正常处理。
高度准确的 L1D 命中/未命中预测还可以带来其他好处。如果预测负载在 L1D 中丢失,则可以抑制相关 uop 的调度程序唤醒信号以避免可能的重播。此外,一旦物理地址可用,就可以在完全解析预测之前尽早将其发送到 L2 缓存。我不知道 AMD 是否采用了这些优化。
utag 可能在两个方向上都是错误的:它可以
预测访问何时会丢失命中,它可以预测何时访问未命中
访问可能已经命中。在任何一种情况下,向 L2 的填充请求
当 L2 响应
填写请求。
在支持多个线性地址空间或允许在同一地址空间中使用同义词的操作系统上,缓存行只能使用物理地址唯一标识。如前所述,当在 utag 内存中查找 utag 时,可能有一次命中或零次命中。首先考虑命中案例。这种基于线性地址的查找会导致推测性命中,但仍需要验证。即使分页被禁用,utag 仍然不是完整地址的唯一替代品。一旦 MMU 提供了物理地址,就可以通过将来自预测路径的物理标签与来自访问的物理地址的标签进行比较来验证预测。可能会出现以下情况之一:
- 物理标签匹配,推测命中被视为真实命中。除了可能触发预取或更新行的替换状态外,无需执行任何操作。
- 物理标签不匹配,并且目标行不存在于同一集合的任何其他条目中。请注意,目标行不可能存在于其他集合中,因为所有 L1D 存储器都使用相同的集合索引功能。稍后我将讨论如何处理。
- 物理标签不匹配,并且目标行确实存在于同一集合的另一个条目中(与不同的 utag 相关联)。稍后我将讨论如何处理。
如果在 utag 内存中没有找到匹配的 utag,则将没有物理标签可比较,因为无法预测。可能会出现以下情况之一:
- L1D 中实际上不存在目标线,因此推测性未命中是真正的未命中。该行必须从其他地方获取。
- 目标行实际上存在于同一个集合中,但具有不同的 utag。稍后我将讨论如何处理。
(我在这里做了两个简化。首先,加载请求被假定为可缓存内存。其次,在 L1D 中的推测或真实命中时,数据中没有检测到错误。我正在尝试继续关注第 2.6.2.2 节。)
仅在情况 3 和 5 中才需要访问 L2,而在情况 2 和 4 中不需要。确定是哪种情况的唯一方法是将负载的物理标签与所有当前线路的物理标签进行比较同一套。这可以在访问 L2 之前或之后完成。无论哪种方式,都必须避免在 L1D 中存在同一行的多个副本的可能性。在访问 L2 之前进行检查可以改善情况 3 和 5 的延迟,但会损害情况 2 和 4。访问 L2 之后进行检查可以改善情况 2 和 4 的延迟,但会损害情况 3 和 5。可以同时执行检查并向 L2 发送请求。但这可能会在情况 3 和 5 中浪费能量和 L2 带宽。似乎 AMD 决定在从 L2(包括 L1 缓存)获取行之后进行检查。
当线路从 L2 到达时,L1D 不必等到它被填充后才响应请求的数据,因此可以容忍更高的填充延迟。现在比较物理标签以确定发生了 4 种情况中的哪一种。情况4,该行以替换策略选择的方式填充到数据内存、标签内存和utag内存中。在情况 2 中,请求的线路替换了恰好具有相同 utag 的现有线路,并且替换策略不参与选择方式。即使同一集合中有一个空条目也会发生这种情况,这实际上会降低缓存的有效容量。在情况 5 中,utag 可以简单地被覆盖。情况 3 有点复杂,因为它涉及一个带有匹配物理标签的条目和一个带有匹配 utag 的不同条目。其中一个必须失效,另一个必须更换。在这种情况下也可能存在一个空条目并且未被利用。
当映射两个不同的线性地址时会发生线性别名
到同一个物理地址。这可能会导致性能损失
加载并存储到别名缓存行。加载到一个地址
在 L1 DC 中有效,但在不同的线性别名下会看到
L1 DC 未命中,需要进行 L2 缓存请求。延迟
通常不会大于 L2 缓存命中的值。然而,如果
多个别名加载或存储同时进行,它们
每个人在更新 utag 时都可能会遇到 L1 DC 未命中
特定的线性地址并从其中删除另一个线性地址
能够访问缓存行。
这就是第 5 种情况(以及较小程度的第 2 种情况)的发生方式。线性别名可以发生在相同的线性地址空间内和不同的地址空间(上下文切换和超线程效应开始发挥作用)。
也有可能两个不同的线性地址不是
别名为相同的物理地址以在 utag 中发生冲突,如果它们
具有相同的线性哈希。在给定的 L1 DC 指数 (11:6) 下,只有一个
具有给定线性哈希的缓存线可以随时访问;任何
具有匹配线性哈希的缓存行在 utag 中被标记为无效
并且无法访问。
这就是案例 2 和 3 的发生方式,它们的处理方式如前所述。这部分讲述了L1D使用简单集索引功能;设置的数字是位 11:6。
我认为大页面使情况 2 和 3 更有可能发生,因为 utag 散列函数使用的位中有一半以上成为页面偏移量而不是页码的一部分。多个操作系统进程之间共享的物理内存使情况 5 更有可能。