performance - Meltdown 缓解与 `calloc()` s CoW "lazy allocation"相结合,是否意味着 calloc() 分配的内存会受到性能影响?

标签 performance memory-management cpu-architecture calloc page-fault

所以calloc()通过向操作系统询问一些虚拟内存来工作。操作系统与 MMU 协同工作,并巧妙地以虚拟内存地址进行响应,该地址实际上映射到 copy-on-write, read-only page full of zeroes .当程序尝试写入该页面的任何位置时,就会发生页面错误(因为您无法写入只读页面),会创建该页面的副本,并且您的程序的虚拟内存被映射到这些全新的副本零。
既然 Meltdown 是一回事,操作系统已被修补,因此不再可能跨内核用户边界进行推测性执行。这意味着每当用户代码调用内核代码时,它都会有效地导致管道停顿。通常,当管道在循环中停滞时,对性能来说是毁灭性的,因为 CPU 最终会浪费时间等待数据,无论是来自缓存还是主内存。
鉴于此,我想知道的是:

  • 当程序写入以前从未访问过的页面时,该页面已分配给 calloc() ,并且重新映射到新的 CoW 页面,这是在执行内核代码吗?
  • 页错误写时复制功能是在 OS 级别还是 MMU 级别实现的?
  • 如果我打电话calloc()分配 4GiB 的内存,然后在一个紧密的循环中用一些任意值(比如 0xFF 而不是 0x00 )初始化它,我的(Intel)CPU 每次写入新的内存时都会达到推测边界吗?页?
  • 最后,如果它是真实的,是否存在这种影响对现实世界性能显着的情况?
  • 最佳答案

    你的前提是错误的。页面错误从来没有流水线化/ super 便宜。但是,随着系统调用和所有其他用户-> 内核转换,Meltdown(和 Spectre)缓解确实使它们变得更加昂贵。

    跨内核/用户边界的推测执行是不可能的 ; Intel CPU 不会重命名权限级别,即内核/用户转换始终需要完整的管道刷新。我认为您误解了 Meltdown:这纯粹是由用户空间和 delayed handling of the privilege checks on TLB hits 中的推测执行引起的。 .

    这在 CPU 设计中是通用的,AFAIK。我不知道任何重命名特权级别或以其他方式推测内核代码、x86 或其他方式的微体系结构。

    Meltdown 缓解增加的成本是进入内核会刷新 TLB。 (或者在具有 TLB 进程上下文 ID 支持的 CPU 上,内核可以使用 PCID 来使内核与用户空间使用单独的页表便宜得多)。

    内核入口点(在 Linux 上)变成了一个蹦床,它交换页表并跳转到真正的内核入口点,以避免将内核 ASLR 偏移量暴露给用户空间。但除此之外还有一个额外的 mov cr3, reg在进入和退出内核时(设置一个新的页表),没有其他任何改变。

    (Spectre 缓解也很棘手,需要更多的更改,例如 retpolines ......并且还可能显着增加 user->kernel->user 的成本。关于页面错误成本的 IDK。)

    @BeeOnRope 报告(见评论和他的完整细节回答)没有 Spectre 补丁,只应用了 Meltdown 补丁,但 nopti “禁用”它的启动选项,增加了 Skylake CPU 上内核的往返成本(使用 syscall 和伪造的 RAX,立即返回 -ENOSYS)从 ~100 到 ~300 个周期。所以这可能是蹦床的成本? 启用实际的页表隔离后,它上升到约 700 个周期 .那根本就没有 Spectre 缓解补丁。 (此外,这是 x86-64 syscall 入口点,而不是页面错误。不过它们可能很相似。)

    缺页异常 :

    CPU 不会预测页面错误,因此无论如何它们都无法推测性地执行处理程序 .页面错误入口点的预取或解码可能会在管道刷新时发生,但该过程在页面错误指令试图退出之前不会启动。错误的加载/存储被标记为在退出时生效,并且不会重新引导前端; Meltdown 的整个关键是在故障负载达到退役之前缺乏对它的操作。

    相关:When an interrupt occurs, what happens to instructions in the pipeline?

    另外:Out-of-order execution vs. speculative execution有一些关于哪种推测真正导致 Meltdown 以及 CPU 如何处理故障的详细信息。

    When a program writes to a never-before-accessed page which was allocated with calloc(), and the remapping to the new CoW page occurs, is this executing kernel code?



    是的,页面错误由内核的页面错误处理程序处理。写时复制没有纯硬件处理。

    If I call calloc() to allocate 4GiB of memory, then initialize it with some arbitrary value (say, 0xFF instead of 0x00) in a tight loop, is my (Intel) CPU going to be hitting a speculation boundary every time it writes to a new page?



    是的。内核不会对归零的页面进行故障排除(与当页面缓存中的数据很热时文件支持的映射不同)。因此,每个新页面都会导致页面错误,即使对于 4k 的普通页面也是如此。 (感谢@BeeOnRope 提供有关此的准确信息。)使用匿名大页面,您每 2MiB (x86-64) 只会出现一次页面错误,这要好得多。

    如果您想避免每页成本,请使用 mmap(MAP_POPULATE) 进行分配在 Linux 系统上将所有页面预置到硬件页表中。我不确定 madvise可以为您预置页面,例如madvise(MADV_WILLNEED)在已经映射的区域上。但是madvise(MADV_HUGEPAGE)将鼓励内核使用匿名大页面(并且可能对物理内存进行碎片整理以释放连续的 2M 块以启用该功能,如果您没有将其配置为在没有 madvise 的情况下执行此操作)。

    相关:Two TLB-miss per mmap/access/munmap有一些 perf在带有 KPTI 补丁的 Linux 内核上的结果。

    关于performance - Meltdown 缓解与 `calloc()` s CoW "lazy allocation"相结合,是否意味着 calloc() 分配的内存会受到性能影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50191769/

    相关文章:

    c++ - 是否可以更快地执行更复杂的循环?

    c++ - 常量引用优于非常量引用的性能

    performance - 在 IntelliJ 上运行 JProfiler for grails

    c# - PowerShell 脚本效率

    ios - 如何清除使用 Kingfisherin UITableView 加载的图像的内存和磁盘缓存?

    x86-64 - 在 x86-64 上,系统崩溃时 “movnti” 或 "movntdq"指令是原子指令吗?

    mysql - 防止 SQL 语句在 `Performance Reports` 部分被 MySQL 的工作台截断

    c - ANSI C 在创建结构时必须使用 malloc() 吗?

    iphone - iPhone OS 应用程序的可用内存

    android - 如何为特定CPU架构创建apk文件