今天发送出去,并且已经合并到正在开发的 Linux 6.18 内核中的最新一批 64 位 ARM”ARM64″架构修复。最值得注意的是,修复了一个发现的”灾难性性能问题”。
今天的 ARM64 拉取请求中吸引我注意的段落是:
“另一个有趣的修复是解决了我们在 per-cpu 原子操作中发现的一个灾难性性能问题,该问题由 Paul 在 SRCU 锁定代码中发现,但需要与硬件人员的一些交互才能解决。”
灾难性性能问题?当我查看补丁和邮件列表讨论时,它始于 Linux 开发者 Paul McKenney 在这条线程中的帖子:
“为了让事件跟踪对 PREEMPT_RT 内核安全,我一直在创建使用 per-CPU 原子操作的优化版 SRCU 读取器。这效果相当好,但在 ARM Neoverse V2 上,我观察到 srcu_read_lock()/srcu_read_unlock() 对的性能约为 100ns,或单个 per-CPU 原子操作的性能约为 50ns。这与 x86 上几纳秒的性能相似,在 ARM 上也类似,例如 atomic_set(&foo, atomic_read(&foo) + 1) 的性能。”
结果表明,在 ARM64 上的每个 CPU 原子操作非常昂贵。从随后的讨论中可以看出,如果禁用 Linux 内核对 ARMv8.1 引入的大系统扩展(LSE)原子指令的支持,性能并不会那么糟糕。
Linux 开发者 Willy Tarreau 也指出,在 ARM 64 位硬件上使用昂贵的原子操作:
这太有趣了!我盲目地将类似的更改应用于 haproxy 中的所有原子操作,并在 80 核的 Ampere Altra(neoverse-n1)上看到持续 2-7%的性能提升,具体取决于测试。在那里,我们也大量使用原子操作来读取/更新主要是局部变量,因为我们尽可能避免共享。我相当确定在某些情况下它确实会带来负面影响,而且我们没有像这里这样的 per_cpu 变体,但这让我想到可以添加一个”主要是局部”的变体,我们可以根据上下文选择。
简而言之,解决方案是,为了解决高延迟问题,这个补丁现在已合并到 ARM64 中,用于非返回的每个 CPU 原子操作使用加载 LSE 原子指令:
“非返回的每个 CPU this_cpu_*() 原子操作在 FEAT_LSE 可用时实现为 STADD/STCLR/STSET。在许多微架构实现中,这些指令倾向于在互连或内存子系统“远处”执行(除非数据已经在 L1 缓存中)。当存在竞争时,这通常更高效,因为它避免了在 CPU 之间来回传递缓存行。另一方面,加载原子指令(例如不带 XZR 作为目标的 LDADD),倾向于在“近处”执行,数据被加载到 L1 缓存中。”
STADD 连续执行 srcu_read_{lock,unlock}*()由于在多个 CPU 实现上的默认发布行为导致额外的开销。由于每个 CPU 的原子操作不太可能在同一内存位置上并发使用,鼓励硬件执行它们”靠近”通过发布带未使用目标寄存器(但不是 XZR)的加载原子操作 – LDADD/LDCLR/LDSET – 来执行。”
因此,最初报告的更高延迟对于这个”灾难性性能问题”来说现在比之前更加合理。
ARM64 修复程序今天合并到 Linux 6.18-rc6 内核中,该内核将在周日发布,然后在 11 月底左右发布 Linux 6.18 稳定版。
转自 Linux 6.18 Merges Fix For “Catastrophic Performance Issue” On 64-bit ARM – Phoronix
Linuxeden开源社区