不覆盖完整 4GiB 线性地址 space 的 IA-32 段描述符是否更慢?

Are IA-32 segment descriptors that do not cover the full 4GiB linear address space slower?

我想知道使用不覆盖整个线性地址的段描述符 space 是否比使用覆盖整个线性地址的段描述符慢?

我希望速度没有差异。

从技术上讲不是:具有更小的限制并不慢 AFAIK。

但是在像 Skylake 这样的现代主流 CPU 上,拥有 non-zero 基础会使 load-use 延迟增加 1 个周期。 (即用于地址生成的额外周期。)CPU 硬件有一条乐观的快速路径可以在正常情况下跳过该加法,因为它为零,否则它是加载延迟的关键路径的一部分。

这在 pointer-chasing 场景(如链表、(二叉)树)和其他情况下最为明显,在这些情况下,准备好的加载地址是数据的关键路径依赖链的一部分,具体取决于该加载。否则 out-of-order exec 几乎可以隐藏它。

Load-use 现代英特尔 CPUs 上 GP-integer regs 的延迟对于 L1d 命中是 5 个周期(零段基数),因此额外的 1 个周期对于对此敏感的工作负载。 (或者在某些 Intel CPU 上 pointer-chasing 用例中的 4 个周期(当基本 reg 本身来自负载时):请参阅 以了解有关使用基本 reg 进行乐观 TLB 查找的详细信息而不是等待正确的 AGU 结果。但我认为他们为 Ice Lake 放弃了它,所以它总是 5 个周期。)

Non-zero 段基数仍用于 thread-local 存储,因此 CPU 仍然支持它们而不会造成灾难性的性能损失。(例如,在某些情况下不会像 sub-normal FP 值那样陷入微代码辅助。)但它确实会消耗一些性能。


参见 Agner Fog 的微架构指南(https://agner.org/optimize/), and other links in https://whosebug.com/tags/x86/info。另外我认为英特尔的优化手册针对英特尔 CPU 提到了这一点;我还没有检查 AMD 的。

例如对于 AMD K8/K10,Agner 写道:

AMD manuals say that the branch misprediction penalty is 10 clock cycles if the code segment base is zero and 12 clocks if the code segment base is nonzero. In my measurements, I have found a minimum branch misprediction penalty of 12 and 13 clock cycles, respectively.

回复:K8/K10 上的数据 load/stores:

The time it takes to calculate an address and read from that address in the level-1 cache is 3 clock cycles if the segment base is zero and 4 clock cycles if the segment base is nonzero, according to my measurements. Modern operating systems use paging rather than segmentation to organize memory. You can therefore assume that the segment base is zero in 32-bit and 64-bit operating systems (except for the thread information block which is accessed through FS or GS). The segment base is almost always nonzero in 16-bit systems in protected mode as well as real mode

我实际上没有在 Agner 的指南中看到 Intel CPUs 的额外延迟,因为它与大多数现代用途无关。所以查看 Intel 的优化指南。


如果您使用的是分段内存模型,您最终可能会偶尔使用 segment-override 前缀;一些 CPUs 对它们可以有效解码的单个指令的前缀数量有限制,但至少 32 位模式不会有 REX 前缀。 pmovzxbd xmm0, [es: eax] 将使用 3 个前缀(2 个作为 SSE4.1 指令的一部分是强制性的,加上 ES)和一个转义字节,这对早期 Silvermont-family 来说是个问题(但我认为以后不会 low-power 核心)。在 32 位模式下,您没有 REX 前缀,因此有助于避免该限制。

Agner Fog 的微架构指南说,在大多数 CPU 上解码段覆盖没有特别的惩罚。

此外,Intel CPUs(Core 2、Nehalem 和 Sandybridge-family 至少)似乎 rename 段寄存器,所以像 mov es, eax 这样修改它们不必序列化 out-of-order exec. 但它仍然不便宜,比如 front-end 上的 10 fused-domain uops Skylake,具有 18c 吞吐量。 (但可以通过管道写入其他段寄存器)。有关详细信息和测试结果,请参阅