缓存集大小不是 2 的幂

Bon*_*ero 5 x86 cpu-architecture tlb cpuid amd-processor

我有一台配备 Ryzen 7 1800X CPU 的 Linux 计算机。根据 WikiChip 的说法,它的 L2-DTLB 有 1536 个条目。所以我假设关联性可以被 3 整除。我编写了一个小程序来检查 CPUID 报告的关联性。有趣的是,它给了我 8 的关联性。为什么呢?这将给出 192 个条目的集合大小,因此没有简单的模 2 次幂索引。那么该指数是如何有效计算的呢?

那是我的程序:

#include <iostream>
#if defined(_MSC_VER)
    #include <intrin.h>
#elif defined(__GNUC__)
    #include <cpuid.h>
#endif

using namespace std;

unsigned cpuid( unsigned (&cpuidRegs)[4], unsigned code, unsigned ex );

int main()
{
    static unsigned const SHORT_WAYS[0x10] = { 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, (unsigned)-1 };
    unsigned regs[4];
    cpuid( regs, 0x80000006u, 0 );
    unsigned n = regs[1] >> 16 & 0xFFF, ways = SHORT_WAYS[regs[1] >> 28];
    cout << "L2 D-TLB: " << n << " / " << ways << " ways" << endl;
}

 inline
unsigned cpuid( unsigned (&cpuidRegs)[4], unsigned code, unsigned ex )
{
#if defined(_MSC_VER)
    __cpuidex( (int *)cpuidRegs, code, ex );
#elif defined(__linux__)
    __cpuid_count(code, ex, cpuidRegs[0], cpuidRegs[1], cpuidRegs[2], cpuidRegs[3]);
#endif
    return cpuidRegs[0];
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 3

AMD 2017 年的优化手册称 Zen 1 的 L2dTLB 是 12 路关联、1536 个条目,位于第 26 页顶部的2.7.2 L2 Translation Lookaside Buffers 部分。该文档名义上是关于 Epyc 7001 系列,但这些是与 Ryzen 相同的 Zen 1 核心。

L2 i TLB 是 8 路关联的。
(512 条目,适用于 4k 或 2M 条目,其中 1G 页面条目“粉碎”为 2M 条目。)

但假设您正在检查正确的级别,8000_0006h则该字段中似乎没有 12 路关联性的编码。不幸的是,它只是可能值表的代码,而不是整数位字段。

由于(AFAIK)无法对 12 路 L2 dTLB 进行编码,也许 AMD 只是选择对最高值 <= 实际值进行编码,因此任何使用它作为调整参数的代码 re:避免混叠将无法冲突未命中的数量超出预期。

表示“改为查看级别 8000_001Dh”的编码1001b(可能)不可用,因为该级别仅适用于普通缓存,不适用于 TLB。

但实际上比这更有趣。Hadi Brais 对此答案评论说,它不仅仅是一个“简单”的 12 路关联 TLB,而且也不是完全分离的。相反,它被分解为4K 条目的 8 路、2M/4M 的 2 路以及合并 32K 组 4K 页面的 2 路。或者在服务器 CPU 上,细分为 6/3/3,CPUID 转储报告 4k 为 6-way,2M 为 3-way。

我发现这篇文章概述了“倾斜”TLB 背后的想法。显然,它确实对不同的大小有不同的方法,但使用散列函数进行索引,而不是仅仅使用几个低位,与用于 2 路关联子集的简单索引方案相比,减少了冲突丢失。

哈迪写道:

手册和 cpuid 信息都提供了正确的 L2 DTLB 关联性和条目数。从 Zen 开始,L2 DTLB 是倾斜的统一缓存。这意味着对于具有特定地址和大小(在查找时未知)的页面,可以根据映射函数将其映射到总共12路中的某些路子集。对于桌面/移动型号,例如 Ryzen 7 1800X,任何 4KB 页面都可以映射到 12 种方式中的 8 种方式,任何 2MB/4MB 页面可以映射到其他 2 种方式,任何合并的 32KB 页面可以映射到其他 2 种方式方法。总共有12种方式。

对于服务器型号,映射分别为 6/3/3。对于以前使用拆分 TLB 的 uarches,cpuid 报告 TLB 信息的方式是明确的。AMD 希望在 Zen 中新的统一倾斜设计中使用相同的格式,但是,正如您所看到的,它并不真正适合。不管怎样,实际上,它确实是一个具有 1536 个条目的 12 路缓存。您只需要知道它是有偏差的就可以正确解释 cpuid 信息。PDE 也缓存在 L2 DTLB 中,但它们的工作方式不同。


AMD 可能已经发布了有关 Zen 上 L2dTLB 关联性的 CPUID 编码的勘误表或其他文档。


顺便说一句,不幸的是,Wikichip 的Zen页面没有列出 TLB 每个级别的关联性。但https://www.7-cpu.com/cpu/Zen.html确实列出了与 AMD 的 PDF 手册相同的关联性。


这将给出 192 个条目的集合大小,因此没有简单的模 2 次幂索引。

事实上,如果这真的可以有效的话,那将需要一些技巧。

或者例如,@Hadi 在关于Ice Lake 48KiB L1 数据缓存的索引如何工作?分割设计是可能的,例如 32k 和 16k 缓存。(但实际上英特尔确实将关联性增加到了 12 路,保持组数相同且为 2 的幂,在保持 VIPT 性能的同时也避免了混叠问题。)

这实际上是一个非常相似的问答,但错误的关联性来自手册而不是 CPUID。CPU 有时确实存在 CPUID 报告有关缓存/TLB 参数的错误信息的错误;想要使用 CPUID 信息的程序应该有每个 CPU 模型/步进的修复表,这样您就可以更正无法通过微代码更新修复的勘误表。

(尽管在这种情况下,由于编码限制,它可能无法真正修复,除非定义一些未使用的编码。)

  • 手册和“cpuid”信息都提供了正确的 L2 DTLB 关联性和条目数。从 Zen 开始,L2 DTLB 是倾斜的统一缓存。这意味着对于具有特定地址和大小(在查找时未知)的页面,可以根据映射函数将其映射到总共12路中的某些路子集。对于桌面/移动型号,例如 Ryzen 7 1800X,任何 4KB 页面都可以映射到 12 种方式中的 8 种方式,任何 2MB/4MB 页面可以映射到其他 2 种方式,任何合并的 32KB 页面可以映射到其他 2 种方式方法。总共有12种方式。 (2认同)
  • 对于服务器型号,映射分别为 6/3/3。对于以前使用拆分 TLB 的 uarches,“cpuid”报告 TLB 信息的方式是明确的。AMD 希望在 Zen 中新的统一倾斜设计中使用相同的格式,但是,正如您所看到的,它并不真正适合。不管怎样,实际上,它确实是一个具有 1536 个条目的 12 路缓存。您只需要知道它是有偏差的就可以正确解释“cpuid”信息。PDE 也缓存在 L2 DTLB 中,但它们的工作方式不同。 (2认同)