此代码只是循环通过一个 2MB 缓冲区,将 0 写入其中的每个字节,并计算执行每次写入所需的时间,更新低水位线和高水位线(min 和 @987654323 @) 显示每次写入所需的最短和最长时间。
假设这个程序是唯一在 CPU 上运行的程序,并且假设在它运行时没有发生异步事件(硬件中断或定时器中断),这个程序将向您显示对内存进行字节宽写入的标称时间,以及处理 TLB 未命中异常和/或页面错误异常所需的最长时间。
TLB 未命中异常是当程序尝试访问 MMU 中没有 TLB 条目的内存时内核发生的异常。 MMU 是 Core Avenue 和 Memory Lane 交叉口的警察,负责将交通引导到应该去的地方。好吧,这是一个可怕的类比。 MMU(内存管理单元)有两个主要用途:1)将虚拟内存访问路由到适当的物理内存地址,以及 2)强制只读、读写、读取执行、只执行等权限,以便对具有冲突属性的虚拟内存区域(或未映射的虚拟内存区域)的杂散指针访问将被捕获并引发内存访问异常(例如 Linux 上的 SIGSEGV)。 TLB 条目是 MMU 中的一组硬件寄存器,它告诉 MMU 当前加载到物理内存中的虚拟内存页面或页面组的权限。但是一个 MMU 没有无限数量的 TLB 条目。它没有足够的 TLB 条目来描述所有内存页面的属性。因此,如果您尝试从进程的地址空间访问一个合法地址,该地址空间没有描述其所在页面的当前 TLB 条目,您将收到 TLB 未命中异常。 TLB 未命中异常处理程序然后从主存储器中获取正确的 TLB 条目数据,并将其写入 MMU 中的 TLB 条目; MMU 甚至可能有一些内置机制来告诉 TLB 未命中异常处理程序它应该使用哪个 TLB 条目......可能是最近最少使用的条目,这是在不久的将来最有可能不再需要的条目.
页面错误类似于 TLB 未命中异常,除了在这种情况下,该虚拟内存页面的内容甚至不在物理内存中......它可能完全不存在(新映射的内存页面),或者它可能先前已被换出到磁盘,以便在有限的物理内存中为程序在某些时候需要的另一页虚拟内存腾出空间。虽然 TLB 未命中异常通常非常快(但确实会影响性能),但如果必须从磁盘(甚至从 SSD 中)拉出页面,则页面错误异常可能会对性能造成巨大影响,因为磁盘存储通常是比内存访问慢一个数量级(或更糟!)。出于这个原因,为了让 CPU 忙于处理一些有用的事情,操作系统的页面错误异常处理程序通常会导致当前运行的进程换出以运行不同的进程(位于“就绪”状态),等待从磁盘接收数据以填充请求的虚拟内存页面。
现在,回到这个“测试代码”及其结果的功效:
此测试取决于操作系统+运行时未在调用malloc(N) 时预先分配内存页面。我相信这可能是典型的行为;即使运行时已经分配了那么多内存并且知道它分配的地址范围,操作系统通常不会分配该内存的实际页面,直到您的程序实际访问(读取或写入)给定页面中的地址。在许多平台上页面为 4KB,但也可能更大,例如在较新的 Intel Pentium 衍生产品上为 4MB。
因此,假设您平台的页面大小为 4KB(4096 字节),当您的程序遍历分配的 2MB 空间时,一次将0 写入一个字节,它将遍历这些 4KB 页面中的 1024 个。因此,这些写入中的 4193280 应该“尽可能快地”发生(不会触发 TLB 未命中或页面错误异常)。其中多达 1024 个将触发 TLB 未命中和/或页面错误异常。因此,鉴于写入的地址位于已加载的虚拟内存页面中,并且其 TLB 条目当前位于 MMU 中,“最小”时间给出了执行写入的最快时间。 “最大”时间给出了执行写入的最坏时间,大概是驻留在尚未映射到物理内存的页面中的地址(并且触发了页面错误异常,并且可能还触发了 TLB 未命中异常)。
这个测试有两个问题,如果我们依赖它的结果来揭示底层硬件的一些特征: 1) 由于其他原因,这个代码本身忽略了进程交换和/或硬件中断的影响,例如时间片和“在后台”接收和处理的网络数据包(这可能会中断正在运行的进程)。并且... 2) 2MB 的测试缓冲区甚至不及较新的 Intel 处理器的 MMU 的 4MB 页面大小。我不知道什么条件决定了操作系统是选择使用 4KB 页面还是 4MB 页面,所以这可能是也可能不是您系统的一个因素。请注意,如果您的 min 和 max 彼此处于同一数量级,那么您可能使用的是具有 4MB 页面的系统,并且如果您的 min 和 max 相差一个订单量级或更大,差异可能不完全归因于 TLB 未命中和页面错误异常。也许这就是为什么作者在他的声明中对代码“可能向您显示一些性能影响......”(添加了重点)有所保留。