Linux 6.18 合并修复 64 位 ARM 的“灾难性性能问题”

发表于:昨天 12:41 8
Linux 6.18 合并修复 64 位 ARM 的“灾难性性能问题”



今天发布并已合并到开发中的 Linux 6.18 内核的是最新一批 64 位 ARM(ARM64)架构修复。最引人注目的是修复了一个被发现的“灾难性性能问题”。

在今天的 ARM64 拉取请求中,这段描述引起了我的注意:“另一个有趣的修复是解决了一个灾难性的性能问题,该问题由 Paul 在 SRCU 锁定代码中发现,但需要与硬件团队进行一些交互才能解决。”

灾难性性能问题?在深入研究补丁和邮件列表讨论时,问题始于 Linux 开发者 Paul McKenney 的这个帖子:“为了使事件跟踪在 PREEMPT_RT 内核中安全,我一直在创建使用每 CPU 原子操作的优化版 SRCU 读取器。这工作得很好,但在 ARM Neoverse V2 上,我看到 srcu_read_lock()/srcu_read_unlock() 对大约需要 100 纳秒,或者单个每 CPU 原子操作大约需要 50 纳秒。相比之下,在 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%。在那里,我们也大量使用原子操作来读取/更新主要是局部变量,因为我们尽可能避免共享。我相当确定在某些情况下这会带来损害,而且我们没有像这里这样的每 CPU 变体的区分,但这让我考虑添加一个‘主要是局部’的变体,我们可以根据上下文选择。”

长话短说,为了解决高延迟问题,这个补丁现已合并到 ARM64,为非返回的每 CPU 原子操作使用加载 LSE 原子操作:“当 FEAT_LSE 可用时,非返回的每 CPU this_cpu_*() 原子操作被实现为 STADD/STCLR/STSET。在许多微架构实现中,这些指令倾向于在互连或内存子系统中‘远’执行(除非数据已经在 L1 缓存中)。这通常在存在争用时更高效,因为它避免了 CPU 之间缓存行的弹跳。另一方面,加载原子操作(例如,目标寄存器不是 XZR 的 LDADD)倾向于‘近’执行,数据加载到 L1 缓存中。像在 srcu_read_{lock,unlock}*() 中那样连续执行的 STADD 会因多个 CPU 实现上的默认发布行为而产生额外开销。由于每 CPU 原子操作不太可能在相同内存位置上并发使用,通过发出加载原子操作——LDADD/LDCLR/LDSET——并让目标寄存器未使用(但不是 XZR),鼓励硬件‘近’执行它们。”

这样一来,最初报告的高延迟比以前合理得多,解决了这个原本的“灾难性性能问题”。ARM64 修复已于今天合并,领先于周日的 Linux 6.18-rc6 内核,然后 Linux 6.18 稳定版预计在 11 月底左右发布。



原文链接:Linux 6.18 Merges Fix For "Catastrophic Performance Issue" On 64-bit ARM
收藏
送赞
分享

发表回复