【问题标题】:Why doesn't copy_user_enhanced_fast_string use AVX if it is available?如果可用,为什么 copy_user_enhanced_fast_string 不使用 AVX?
【发布时间】:2019-12-30 04:34:50
【问题描述】:

在了解我的应用程序的分析结果(I/O-heavy)时,我面临copy_user_enhanced_fast_string 是最热门的区域之一。在用户空间和内核空间之间复制时调用它。 x86 上的The implementation 看起来像:

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)

为什么不使用vmovaps/vmovups?难道没有证明 AVX 在复制可用的地方没有性能优势吗?

【问题讨论】:

  • "一些 CPU 正在添加增强型 REP MOVSB/STOSB 指令。如果启用,建议使用增强型 REP MOVSB/STOSB。"
  • 也许有一种方法可以使用 mmap'ed IO 并避免完全复制。
  • @TrentP 将文件mmap 然后memcpy (使用AVX)到某个正确的位置可能是个好主意。你是这个意思吗?
  • 就像@Peter Cordes 所说,使用mmap 的最佳方法是不复制数据,而是直接从映射位置使用它。 mmap+copy 不会比 read 好多少。但是,您的问题并未说明您正在访问文件,并且还有很多其他方法可以从内核获取数据。具有高吞吐量的接口,例如使用 V4L 进行视频捕获,通常具有避免复制的 mmap'ed 模式。另请参阅sendfile()splice(),了解使用管道和套接字时避免复制的方法。
  • 使用 mmap+copy,您需要额外的页表操作来将文件映射到进程的地址空间,但可以节省每次 read() 的系统调用模式切换开销。任何可能副本本身都更快。总体上可能更快或更慢。如果您不能复制数据,就会获得收益。

标签: linux assembly linux-kernel x86-64


【解决方案1】:

内核代码只能在kernel_fpu_begin() / kernel_fpu_end() 之间安全地使用 FPU / SIMD 来触发xsave(以及返回用户空间之前的xrstor)。或 xsaveopt 或其他。

这是一个很大的开销,除了少数罕见的情况(如md RAID5 / RAID6 奇偶校验创建/使用)之外不值得。

不幸的是,这意味着大多数内核代码只能使用 GP 整数寄存器。 AVX memcpy 循环和rep movsb memcpy 之间的区别并不值得在每个系统调用上使用 xsave/xrstor。


上下文切换与刚进入内核后台:

在用户空间中,内核在用户空间任务之间的上下文切换中处理状态保存/恢复。在内核中,当您即将返回相同的用户空间时,您希望避免每次进入内核(例如系统调用)时进行繁重的 FPU 保存/恢复,因此您只需保存 GP-integer regs .


对于已知大小的副本,没有 SSE/AVX 并不算太糟糕,尤其是在具有 ERMSB 功能的 CPU 上(这是使用此复制功能的时候,因此名称中有 enhanced_fast_string)。对于中型到大型对齐的副本,rep movsb 至少在 Intel CPU 上几乎一样快,希望 AMD 也一样快。见Enhanced REP MOVSB for memcpy。或者没有 ERMSB,至少有 rep movsq + cleanup。

在 64 位内核中,GP 整数 reg 的大小是 XMM reg 的一半。对于小副本(低于内核的 64 字节阈值),与一般系统调用的开销相比,8x GP 整数 8 字节加载和 8 字节存储应该非常有效。 4x XMM 加载/存储会很好,但这是与保存 FPU 状态的权衡。

对于strlen/strcpy,没有 SIMD 的情况要糟糕得多,其中 pcmpeqb非常 好于一次 4 或 8 字节的 bithack。而 SSE2 是 x86-64 的基线,因此如果不是为了保存 FPU 状态的问题,x86-64 内核可以在没有动态调度的情况下依赖它。

理论上,您可以吃掉 SSE/AVX 转换惩罚,并喜欢一些糟糕的 Windows 驱动程序,只需使用旧版 SSE 指令手动保存/恢复向量 reg 的低 128。 (这就是为什么传统 SSE 指令不将完整 YMM / ZMM 的高字节归零的原因)。如果有人对内核模式 strcpystrlenmemcpy 进行基准测试,则 IDK。

【讨论】:

  • 你能理解为什么在内核空间需要xsave/xrstor,而在用户空间不需要?在用户空间中,我只能调用 avx-sse 转换,可以使用 vzeroupper 解决,以避免保存 avx 寄存器的上部。
  • @St.Antario:在用户空间中,内核在用户空间任务之间的上下文切换中处理状态保存/恢复。在内核中,当您即将返回相同的用户空间时,您希望避免每次进入内核(例如系统调用)时进行繁重的 FPU 保存/恢复,因此您只需保存 GP-integer regs .它与 SSE/AVX 转换完全无关。
  • 因为在我的情况下copy_user_enhanced_fast_string 来自reading 常规文件,似乎可以通过mmaping 相关文件页面然后memcpy(在我的笔记本电脑)它们到一些用户空间分配的缓冲区。这样的事情是常见的吗?
  • @St.Antario:是的,mmap 可能比read 快一些。但是你需要memcpy吗?这可能会吃掉大部分收益。理想情况下,您可以直接使用映射区域,或者至少在复制期间做一些 有用的工作。 (例如,计算某些东西,必要时进行字节交换,转置或任何通常的第一个处理步骤,即使它通常是只读步骤。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-03
  • 2014-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多