如何在不接触缓存的情况下写入或读取内存
Posted
技术标签:
【中文标题】如何在不接触缓存的情况下写入或读取内存【英文标题】:How to write or read memory without touching cache 【发布时间】:2015-04-25 10:15:49 【问题描述】:在 x86 CPU 下,有没有办法在不触及 L1/L2/L3 缓存的情况下写入/读取内存?
x86 CPU 中的缓存是否完全由硬件管理?
编辑:我想这样做是因为我想对内存速度进行采样并查看内存性能的任何部分是否会降低。
【问题讨论】:
如果您在 Google 上在 ANSI C 中基于 x86 的内存上读取和写入内存,您会看到什么?只是好奇。 (我喜欢这个HERE) @ryyker:我得到的第一个链接(相当恰当)是segmentation faults 上的维基页面。 是的,这是分段错误……但我不认为是“在 ANSI C 中基于 x86 的内存上读取和写入内存”会导致分段错误。我想要的是在程序的正确边界内禁用缓存,并写入或读取内存 @i486,我想在内核中采样内存的速度,看看是否有内存性能下降的部分 相关问题:***.com/q/37070/1084 【参考方案1】:CPU 确实在硬件中管理自己的缓存,但 x86 为您提供了一些影响这种管理的方法。
要在不缓存的情况下访问内存,您可以:
使用 x86 非临时指令,它们旨在告诉 CPU 您不会再次重用此数据,因此将其保留在缓存中毫无意义。 x86 中的这些指令通常称为 movnt*(根据数据类型使用后缀,例如用于将普通整数加载到通用寄存器的 movnti)。还有一些流式加载/存储的说明,它们也使用类似的技术,但更适合高 BW 流(当您连续加载整行时)。 要使用这些,可以在内联汇编中对其进行编码,或者使用编译器提供的内在函数,它们中的大多数都将其称为 _mm_stream_*
将特定区域的内存类型更改为不可缓存。既然您声明您不想禁用所有缓存(这是理所当然的,因为这还包括代码、堆栈、页面映射等),您可以将基准数据集所在的特定区域定义为不可缓存,使用 MTRR(内存类型范围寄存器)。有几种方法可以做到这一点,您需要为此阅读一些文档。
1234563整个缓存)。确保正确隔离这些操作,以便您可以保证它们已完成(当然不要将它们作为延迟的一部分来衡量)。话虽如此,如果您只想为内存读取计时而执行所有这些操作,您可能会得到不好的结果,因为大多数 CPU “低效”地处理非临时或不可缓存的访问。如果您只是在强制读取来自内存之后,最好通过顺序访问一个大到不适合任何缓存的数据集来操作缓存 LRU 来实现。这将使大多数 LRU 方案(不是全部!)首先删除最旧的行,因此下次您回绕时,它们必须来自内存。
请注意,要使其正常工作,您需要确保您的硬件预取器没有帮助(并且意外地覆盖了您想要测量的延迟) - 要么禁用它,要么使访问步幅足够大以使其无效。
【讨论】:
请注意,clflush
命令相对较新。我相信它只在服务器中可用。
谢谢!由于理想情况下我会尽量避免修改应用程序的代码,所以 2 和 3 似乎更有帮助。我会试试看!
这里是non-temporal movs in Intel's intrinsics guide的列表【参考方案2】:
Leeor preety 为您的任务列出了最“pro”的解决方案。我将尝试添加另一个可以达到相同结果的提案,并且可以使用简单的代码用纯 C 语言编写。这个想法是制作一个类似于 HPCC Challenge 基准测试中的“全局随机访问”的内核。
内核的想法是在一个巨大的数组中随机跳转 8B 值,通常是物理内存大小的 1/2(因此,如果您有 16 GB 的 RAM,则需要一个8GB 阵列导致 8B 的 1G 元素)。对于每次跳转,您都可以读取、写入或 RMW 目标位置。
这很可能测量 RAM 延迟,因为在 RAM 中随机跳转会使缓存非常低效。您将获得极低的缓存命中率,并且如果您对阵列进行足够的操作,您将能够衡量内存的实际性能。此方法还使预取非常无效,因为没有可检测到的模式。
您需要考虑以下事项:
-
确保编译器不会优化您的内核循环(确保对该数组执行某些操作或使用您从中读取的值进行某些操作)。
使用非常简单的随机数生成器,不要将目标地址存储在另一个数组中(将被缓存)。我使用了 Linear Congruential Generator。这样,下一个地址的计算速度非常快,并且不会增加 RAM 以外的额外延迟。
【讨论】:
谢谢,但我尽量在后台测速,尽量少影响应用程序的性能,所以占用太多内存对我的场合不太好。但是,这是一个基准测试的好主意,我可以用它来评估实现。 请记住,大多数现代“大”CPU(您通常会在智能手机或更大的手机中找到的那种)将允许多个未完成的内存请求。因此,如果您按照建议使用 LCG 之类的东西随机访问一个大型数组,您将无法测量真正的内存访问延迟,因为 CPU 将“排队”N 次访问,这些访问大部分将并行执行。在最近的英特尔 CPU 上,N 大约是 10(谷歌“行缓冲区”),因此您可以测量一个值,该值低至真实延迟的 1/10。要测量真正的延迟,请确保每个内存访问都依赖于之前的访问。 一个简单的方法是让你的数组全为零,然后简单地将最后一次查找的结果添加到 LCG 的结果中。由于它始终为零,因此不会影响结果,但它会强制 CPU 在进行下一个内存访问之前解析每个内存访问。通过使用随机值预先填充数组并将其用作随机函数,您也可以变得更有趣。这消除了时序循环中 LCG 的开销。以上是关于如何在不接触缓存的情况下写入或读取内存的主要内容,如果未能解决你的问题,请参考以下文章
如何在不加载图像的情况下为文件系统上的现有图像写入或修改 EXIF 数据?
如何在不使用用户设置的情况下在运行时读取/写入 app.config 设置?
如何在不修改脚本的情况下计算 PHP 脚本的文件读取和写入?