在某些环境中,某些指令或使用某些寄存器存在限制。例如,在Linux内核中,通常不允许使用SSE/AVX或FP寄存器。因此,大多数优化的 memcpy 变体无法使用,因为它们依赖于 SSE 或 AVX 寄存器,并且在 x86 上使用基于 mov 的普通 64 位副本。对于这些平台,使用rep movsb 可以实现优化memcpy 的大部分性能,而不会打破SIMD 代码的限制。
为什么 x86_64 内核不能使用 SSE/AVX?如果它会变得memcopy()
更快,似乎应该被允许。当我看到这个评论时,我刚刚学习 Intel Assembly,并且特别想学习 SEE/AVX。
对 Linux 内核中的 SSE/MME 和 AVX 优化特别感兴趣。
答案1
作为吉尔斯提到,在任何可能使用 FPU 的地方,内核都需要支持保存和恢复其状态。由于用户空间可以使用 FPU,因此在任何情况下都需要在上下文切换时进行处理(IE,当当前 CPU 从一个线程切换到另一个线程时)——至少,当先前运行的线程使用 FPU 时。那么为什么不将其扩展到内核呢?
避免在内核中使用 FPU 有几个原因:
- 从可移植性的角度来看,某些架构根本不支持在内核中使用FPU,因此通用代码不能依赖它;
- 保存和恢复 FPU 状态的成本很高,并且引入了某些与实现相关的约束(在 x86 Linux 上,抢占尤其需要仔细考虑)。
让内核避免使用 FPU 意味着可以降低用户空间的成本:仅在上下文切换后才需要恢复 FPU 状态返回用户空间时(与上下文切换后立即相反),并且并非在所有情况下(仅当涉及的线程实际使用 FPU 时)。
它是然而,可以在内核中、在特定于 x86 的代码中使用 FPU(和 MMX/SSE/AVX),其中收益大于成本:因此它最终被用在加密代码和 RAID6 中。这些来自 Linus 的电子邮件提供更多细节。如果要使用 FPU,则需要使用kernel_fpu_begin
和之间的代码将所有 FPU 括起来kernel_fpu_end
,并确保它不会出现故障或休眠。看arch/x86/include/asm/fpu/api.h
和arch/x86/kernel/fpu/core.c
了解详情。
对于memcpy
,性能提升不会超过使用 FPU 的成本。
(x86 具有相当复杂的 FPU 架构,但它提供了使操作系统能够共享 FPU 所需的所有功能:每当发出 FPU 指令时它都会捕获,这允许内核针对从不使用 FPU 的进程进行优化它可以指示 CPU 和 FPU 状态何时可能发生分歧。它还提供保存和恢复 FPU 状态的指令,FSAVE
并且FXSAVE
取决于XSAVE
FPU 的版本,这可能是 8086 设计的一个方面。设计师们最有先见之明.)