17

我正在为valgrind 中的 cachegrind/callgrind 工具制作一个小补丁,该补丁将使用完全通用的代码、CPU 指令和缓存配置进行自动检测(现在只有 x86/x64 自动配置,其他架构不提供CPUID 类型配置到非特权代码)。该代码将需要完全在非特权上下文中执行,即纯用户模式代码。它还需要在非常不同的 POSIX 实现中可移植,所以 grokking /proc/cpuinfo 不会做,因为我们的目标系统之一没有这样的东西。

检测 CPU 的频率、缓存的数量、它们的大小,甚至缓存行大小都可以使用 100% 通用 POSIX 代码完成,它没有任何特定于 CPU 的操作码(只是很多合理的假设,例如添加两个数字在一起,如果没有内存或寄存器依赖停顿,可能会在一个周期内执行)。这部分相当简单。

什么不是那么简单,为什么我问 StackOverflow,是如何检测给定缓存的缓存行关联性?关联性是高速缓存中有多少位置可以包含来自主存储器的给定高速缓存行。我可以看到可以检测到 L1 缓存关联性,但是 L2 缓存呢?L1 关联性肯定会妨碍吗?

我很欣赏这可能是一个无法解决的问题。但我把它扔到 StackOverflow 上,希望有人知道我不知道的事情。请注意,如果我们在这里失败,我将简单地以四种方式的关联性默认值进行硬编码,假设它不会对结果产生巨大影响。

谢谢,
尼尔

4

3 回答 3

7

这是一个方案:

具有跨度S的内存访问模式,访问的唯一元素数 = N。该测试首先接触每个唯一元素,然后通过大量访问相同模式来测量访问每个元素的平均时间。

示例:对于 S = 2 和 N = 4,地址模式将为 0,2,4,6,0,2,4,6,0,2,4,6,...

考虑一个多级缓存层次结构。您可以做出以下合理的假设:

  • n+1 级缓存的大小是第 n 级缓存大小的两倍
  • 第 n+1 个缓存的关联性也是第 n 个缓存的关联性的两倍的幂。

这两个假设允许我们说,如果两个地址映射到第 n+1 个缓存(例如 L2)中的同一组,那么它们必须映射到第 n 个缓存(例如 L1)中的同一组。

假设您知道 L1、L2 缓存的大小。您需要找到 L2 缓存的关联性。

  • 设置步幅S = L2 缓存的大小(以便每次访问都映射到 L2 和 L1 中的相同集合)
  • 变化N(2 的幂)

你得到以下制度:

  • 方案1: N <= L1 的结合性。(L1 中的所有访问 HIT)
  • 方案2: L1 的关联性 < N <= L2 的关联性(L1 中的所有访问都未命中,但 L2 中的 HIT)
  • 方案3: N > L2 的关联性(L2 中的所有访问都未命中)

因此,如果您针对 N 绘制平均访问时间(当 S = L2 的大小时),您将看到类似阶梯的图。最低步骤的结尾为您提供 L1 的关联性。下一步为您提供 L2 的关联性。

您可以在 L2-L3 等之间重复相同的过程。请让我知道这是否有帮助。通过改变内存访问模式的步长来获取缓存参数的方法类似于 LMBENCH 基准测试使用的方法。我不知道 lmbench 是否也能推断出关联性。

于 2013-04-14T05:28:35.703 回答
0

你能做一个只访问同一集合中的行的小程序吗?然后你可以增加访问之间的堆栈距离,当执行时间急剧下降时,你可以假设你已经达到了关联性。

它可能不是很稳定,但也许这可以带来领先,不知道。我希望它可以帮助。

于 2013-04-18T11:52:19.233 回答
-3

对于 x86 平台,您可以使用cpuid

有关详细信息,请参阅http://www.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html

你需要类似的东西:

long _eax,_ebx,_ecx,_edx;
long op = func;

asm ("cpuid"
    : "=a" (_eax),
    "=b" (_ebx),
    "=c" (_ecx),
    "=d" (_edx)
    : "a" (op)
);

然后根据上述链接中的文档使用信息。

于 2013-05-06T16:03:30.033 回答