【发布时间】:2020-05-02 21:17:14
【问题描述】:
如果我要运行二进制可执行程序,是否所有指令都必须存储在指令缓存中?指令缓存是 L1、L2 或 L3 CPU 缓存中的一个位置,还是不同的实体?当我运行可执行文件时,我不太清楚物理上会发生什么,任何形式的清晰度将不胜感激。
【问题讨论】:
标签: caching hardware cpu-architecture cpu-cache
如果我要运行二进制可执行程序,是否所有指令都必须存储在指令缓存中?指令缓存是 L1、L2 或 L3 CPU 缓存中的一个位置,还是不同的实体?当我运行可执行文件时,我不太清楚物理上会发生什么,任何形式的清晰度将不胜感激。
【问题讨论】:
标签: caching hardware cpu-architecture cpu-cache
您在磁盘上的可执行文件在逻辑上内存映射到新进程的虚拟地址空间。
与通常使用虚拟内存一样,如果数据实际上不在 DRAM 中(硬页面错误),或者如果数据实际上不在 DRAM 中,但硬件页表尚未“连接”,则触摸可执行文件的 4k 页可能会触发页面错误那个页面。每次重复运行时仍可能发生软页面错误,因为操作系统通常“懒惰”连接页表,即使文件数据位于内核的页面缓存中也是如此。
(x86 使用 4kiB 虚拟内存页面。这是一种常见的页面大小,但其他一些 ISA 使用或可以使用其他页面大小,如 8k 或 16k。)
内核可以通过确保至少包含进程入口点的页面在进入用户空间之前从磁盘加载并连接起来进行优化。否则,它只会立即将页面错误返回内核。例如非常早期的 Linux,比如 0.11,就是这样工作的,作为开发中的一个开始工作的步骤,然后才使它变得良好。
当数据在 DRAM 中时,CPU 会通过 L1i / L2 / L3 缓存进行代码提取。这与数据加载完全相同,但通过 L1 I-cache 而不是 L1 D-cache,假设机器具有拆分 L1 缓存。外层缓存(假设它们存在)几乎总是统一的。现代 x86 CPU 和其他高端芯片(如 POWER)通常具有 3 级缓存,通常 L1i/d 和 L2 是每个内核专用的,并具有大型共享 L3。否则,您可能只有私有 L1i/L1d 和共享 L2,或者在单核系统中可能只有 1 或 2 级缓存。
这些缓存在大多数 CPU 上的行大小为 64 字节(包括自 P4 / Core 2 左右以来的所有 x86 CPU)。缓存未命中不是故障,核心只需等待线路到达。如果仍然没有完成任何乱序执行,那么它仍然可能在代码缓存未命中时发生。但否则 CPU 会卡在无事可做。
(TLB 未命中也是一回事。大多数 ISA 都有硬件页面遍历,使其对软件透明,但 iTLB 未命中同样会停止指令获取,使 CPU 没有任何排队工作要做。所以 i-cache / i-TLB 未命中甚至比数据缓存/d-TLB 未命中更糟糕,其中未命中/未命中命中和无序执行可以让一些有用的工作继续进行。)
在现代 x86 CPU 中,解码指令被缓存在一个很小、非常快的“uop 缓存”中,并将机器代码字节缓存在 L1i 缓存中。
Pentium 4 没有 L1i 缓存,只有一个跟踪缓存,但效果并不好。而且它没有足够的解码器的晶体管或功率预算来快速构建跟踪缓存未命中的跟踪。这是 NetBurst 微架构的几个主要缺点之一。
【讨论】:
指令永久存储在大容量存储设备上的二进制可执行文件中。当程序执行时,可执行文件的内容被加载到主存储器(RAM)中。一般来说,CPU 和 RAM 之间的所有流量都经过缓存;可执行文件包含的指令也是如此。
缓存可能包含指令、数据或两者兼有。对于 Intel CPU,有两个 L1 缓存:L1d 用于数据,L1i 用于指令; L2 和 L3 是共享的。下图非常清楚地说明了这一点:
这是简短的答案。如果您想了解更多信息,周围有很多很好的材料。 Ulrich Drepper 的 What every programmer should know about memory 非常适合阅读。
【讨论】: