St.*_*rio 1 linux assembly x86-64 linux-kernel
在了解我的应用程序(I/O 密集型)的分析结果时,我面临copy_user_enhanced_fast_string成为最热门的区域之一。在用户空间和内核空间之间复制时调用它。x86 上的实现如下所示:
ENTRY(copy_user_enhanced_fast_string)
ASM_STAC
cmpl $64,%edx
jb .L_copy_short_string /* less then 64 bytes, avoid the costly 'rep' */
movl %edx,%ecx
1: rep
movsb
xorl %eax,%eax
ASM_CLAC
ret
.section .fixup,"ax"
12: movl %ecx,%edx /* ecx is zerorest also */
jmp .Lcopy_user_handle_tail
.previous
_ASM_EXTABLE_UA(1b, 12b)
ENDPROC(copy_user_enhanced_fast_string)
Run Code Online (Sandbox Code Playgroud)
为什么不vmovaps/vmovups用于那个?不是已经证明 AVX 在复制可用的地方没有性能优势吗?
内核代码只能在kernel_fpu_begin()/之间安全地使用 FPU / SIMDkernel_fpu_end()来触发xsave(并且xrstor在返回用户空间之前)。或 xsaveopt 或其他什么。
这是一个很大的开销,除了少数罕见的情况(如mdRAID5/RAID6 奇偶校验创建/使用)之外,这是不值得的。
不幸的是,这意味着大多数内核代码只能使用 GP 整数寄存器。 AVX memcpy 循环和rep movsbmemcpy之间的区别不值得在每个系统调用上使用 xsave/xrstor。
上下文切换 vs. 刚进入内核后台:
在用户空间中,内核处理用户空间任务之间上下文切换时的状态保存/恢复。在内核中,当您即将返回相同的用户空间时,您希望避免每次进入内核(例如系统调用)时进行繁重的 FPU 保存/恢复,因此您只需保存 GP-integer regs .
对于已知大小的副本,没有 SSE/AVX 还不错,尤其是在具有 ERMSB 功能的 CPU 上(当使用此复制功能时,因此enhanced_fast_string得名)。对于大中型对齐副本,rep movsb至少在 Intel CPU 上几乎和 AMD 一样快。有关memcpy 的信息,请参阅增强型 REP MOVSB。或者没有 ERMSB,至少有rep movsq+ 清理。
在 64 位内核中,GP 整数 reg 是 XMM reg 大小的一半。对于小副本(低于内核的 64 字节阈值),与一般系统调用的开销相比,8x GP 整数 8 字节加载和 8 字节存储应该非常有效。4x XMM 加载/存储会很好,但这是与保存 FPU 状态的权衡。
没有SIMD是显著恶化的strlen/strcpy在那里pcmpeqb是非常好对在同一时间bithack 4或8个字节。而 SSE2 是 x86-64 的基线,所以如果不是为了保存 FPU 状态的问题,x86-64 内核可以依赖它而无需动态调度。
理论上,您可以吃掉 SSE/AVX 转换惩罚,并像一些糟糕的 Windows 驱动程序一样做,只需手动保存/恢复带有传统 SSE 指令的向量 reg 的低 128。(这就是传统 SSE 指令不会将完整 YMM / ZMM 的高位字节归零的原因)。IDK,如果有人对内核模式strcpy或strlen, 或memcpy.