如何在不触摸缓存的情况下写入或读取内存

Mic*_*ong 11 c memory x86 caching

有没有办法在不触及x86 CPU的L1/L2/L3缓存的情况下写入/读取内存?

是否完全由硬件管理的x86 CPU缓存?

编辑:我想这样做,因为我想采样内存的速度,看看内存的任何部分性能是否下降.

Lee*_*eor 16

CPU确实在硬件中管理自己的缓存,但x86为您提供了一些影响此管理的方法.

要在不缓存的情况下访问内存,您可以:

  1. 使用x86非时间指令,它们是为了告诉CPU你不会再次重用这些数据,因此将它保留在缓存中是没有意义的.x86中的这些指令通常称为movnt*(根据数据类型使用后缀,例如用于将正常整数加载到通用寄存器的movnti).还有用于流加载/存储的指令,这些指令也使用类似的技术,但更适合于高BW流(当您连续加载整行时).要使用它们,要么在内联汇编中对它们进行编码,要么使用编译器提供的内在函数,其中大多数都称为该族_mm_stream_*

  2. 将特定区域的内存类型更改为不可缓存.既然你声明你不想禁用所有缓存(理所当然,因为这也包括代码,堆栈,页面映射等等),你可以将基准测试数据集所在的特定区域定义为不可缓存,使用MTRR(存储器类型范围寄存器).有几种方法可以做到这一点,你需要阅读一些文档.

  3. 最后一个选项是正常获取行,这意味着它最初会被缓存,但是然后使用专用clflush指令强制它清除所有缓存级别(如果要刷新整个缓存,则使用完整的wbinvd).确保正确地围绕这些操作,以便您可以保证它们已完成(当然,不要将它们作为延迟的一部分进行测量).

话虽如此,如果你想做所有这些只是为了时间内存读取,你可能会得到不好的结果,因为大多数CPU"低效率"处理非时间或不可缓存的访问.如果你刚刚强迫读取来自内存,最好通过顺序访问一个足够大的数据集来操作高速缓存LRU来实现这一点,这些数据集不适合任何高速缓存.这将使大多数LRU方案(不是全部!)首先丢弃最旧的行,所以下次你回绕时,它们必须来自内存.

请注意,要使其工作,您需要确保您的硬件预取器没有帮助(并且意外地覆盖了您要测量的延迟) - 要么禁用它,要么使访问步幅足够远以使其无效.


VAn*_*rei 7

Leeor preety列出了最适合您任务的" 专业 "解决方案.我将尝试添加另一个可以实现相同结果的提议,并且可以使用简单的代码在纯C中编写.我们的想法是创建一个类似于HPCC Challenge基准测试中的"全局随机访问"的内核.

内核的想法是随机跳过一大堆 8B值,这些值通常是物理内存的1/2(因此,如果你有16 GB的RAM,你需要一个8GB的阵列,通向8B的1G元素).对于每次跳转,您可以读取,写入或RMW目标位置.

这很可能测量RAM延迟,因为随机跳过RAM 会使缓存效率非常低.您将获得极低的缓存命中率,如果您对阵列进行了足够的操作,您将能够测量内存的实际性能.由于没有可检测的模式,该方法也使得预取非常无效.

你需要考虑以下事项:

  1. 确保编译器不会优化你的内核循环(确保在该数组上执行某些操作或使用从中读取的值创建某些内容).
  2. 使用一个非常简单的随机数生成器,不要将目标地址存储在另一个数组中(将被缓存).我使用线性同余发生器.这样,下一个地址的计算速度非常快,并且不会增加RAM之外的额外延迟.

  • 一种简单的方法是将数组设置为全零,然后将上次查找的结果添加到 LCG 的结果中。由于它始终为零,因此不会影响结果,但会强制 CPU 在继续下一个内存访问之前解析每个内存访问。您也可以变得更奇特,通过用随机值预先填充数组并将其用作随机函数。这消除了定时循环中 LCG 的开销。 (3认同)
  • 请记住,大多数现代“大”CPU(您通常会在智能手机或更大的任何东西中找到的那种)将允许对内存的多个未完成请求。因此,如果您按照建议使用 LCG 之类的东西随机访问一个大型数组,您将无法测量真正的内存访问延迟,因为 CPU 将“排队”N 次访问,而这些访问主要是并行执行的。在最近的 Intel CPU 上,N 大约为 10(谷歌“行缓冲区”),因此您可以测量一个低至真实延迟 1/10 的值。要测量真正的延迟,请确保每个内存访问都依赖于它之前的访问。 (2认同)