50

我有兴趣在 Windows 中强制刷新 CPU 缓存(出于基准测试的原因,我想从 CPU 缓存中没有数据开始进行模拟),最好是基本的 C 实现或 Win32 调用。

有没有一种已知的方法可以通过系统调用甚至像说一个大的那样偷偷摸摸地做到这一点memcpy

Intel i686 平台(P4 及以上也可以)。

4

4 回答 4

55

幸运的是,有不止一种方法可以显式刷新缓存。

指令“wbinvd”写回修改后的缓存内容并将缓存标记为空。它执行一个总线周期以使外部缓存刷新其数据。不幸的是,这是一个特权指令。但是,如果可以在 DOS 之类的环境下运行测试程序,那么这是可行的方法。这具有使“OS”的缓存占用空间非常小的优点。

此外,还有“invd”指令,它使缓存无效而不将它们刷新回主存。这违反了主存和缓存的一致性,所以你必须自己处理。不是很推荐。

出于基准测试的目的,最简单的解决方案可能是将大内存块复制到标有 WC(写入组合)而不是 WB 的区域。显卡的内存映射区域是一个不错的选择,或者您可以通过 MTRR 寄存器自行将区域标记为 WC。

您可以在用于测量时钟周期和性能监控的测试程序中找到一些关于对短例程进行基准测试的资源。

于 2009-11-18T16:20:52.187 回答
9

有 x86 汇编指令强制 CPU 刷新某些缓存行(例如CLFLUSH),但它们非常模糊。特别是 CLFLUSH 仅从所有级别的缓存(L1、L2、L3)中刷新选择的地址。

像说一个大的内存副本一样偷偷摸摸的东西?

是的,这是最简单的方法,并且会确保 CPU 刷新所有级别的缓存。只需从基准测试中排除缓存刷新时间,您就应该很好地了解程序在缓存压力下的执行情况。

于 2009-11-18T15:43:25.663 回答
2

不幸的是,没有办法显式刷新缓存。您的一些选择是:

1.) 通过在您要进行基准测试的代码的迭代之间执行一些非常大的内存操作来破坏缓存。

2.) 在x86 控制寄存器中启用缓存禁用并对其进行基准测试。这也可能会禁用指令缓存,这可能不是您想要的。

3.) 使用非临时指令实现您的代码部分进行基准测试(如果可能的话) 。虽然,这些只是对处理器使用缓存的提示,但它仍然可以自由地做它想做的事。

对于您的目的,1 可能是最简单且足够的。

编辑:糟糕,我纠正了有一条指令使 x86 缓存无效,请参阅 drhirsch 的答案

于 2009-11-18T15:50:57.237 回答
1

x86 指令WBINVD回写并使所有缓存无效。它被描述为

将处理器内部高速缓存中所有已修改的高速缓存行写回主内存,并使内部高速缓存无效(刷新)。然后该指令发出一个特殊功能总线周期,指示外部高速缓存也写回修改后的数据,并发出另一个总线周期以指示外部高速缓存应该无效。

重要的是,指令只能在ring0,即操作系统中执行。所以你的用户态程序不能简单地使用它。在 Linux 上,您可以编写一个内核模块来按需执行该指令。其实已经有人写过这样的内核模块了:https ://github.com/batmac/wbinvd

幸运的是,内核模块的代码非常小,因此您可以在将互联网上陌生人的代码加载到内核之前对其进行实际检查。您可以通过阅读来使用该模块(并触发执行WBINVD指令)/proc/wbinvd,例如通过cat /proc/wbinvd.

但是,我发现这条指令(或者至少是这个内核模块)真的很慢。在我的 i7-6700HQ 上,我测量它需要 750µs!这个数字对我来说似乎真的很高,所以我可能在测量这个时犯了一个错误——请记住这一点!该指令的解释只是说:

WBINVD 完成的时间或周期量将因不同缓存层次结构的大小和其他因素而异。

于 2019-05-10T18:28:47.757 回答