代码之家  ›  专栏  ›  技术社区  ›  Oliv

虚拟内存映射碎片是否导致性能问题?

  •  0
  • Oliv  · 技术社区  · 6 年前

    我有一个应用程序,我从创建大型匿名映射(4MB的顺序)开始,每个映射都使用Linux mmap 系统调用。

    然后,在进程执行期间,为了尽快释放内存,我考虑取消映射较小的内存块,以便最初是大块的虚拟内存映射将导致碎片化。

    这可能会由于虚拟内存转换表碎片而导致性能问题,还是内核使用智能策略来避免?我可以不介意虚拟内存映射的碎片化吗?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Kristina    6 年前

    简短回答: 除非您随机分配大量的最小粒度(即4096字节)的非连续区域,否则不是这样。


    长回答: 有点,

    在现代体系结构中,您有几个层次的虚拟内存映射(或页表,无论您喜欢哪个术语),64位体系结构中,对于48位地址空间,通常有4个层次的内存映射(Intel即将推出的扩展将添加另一个层次,允许将4096字节的页面分割成256字节的页面)。每次在一个还没有映射的区域中分配一个页面时,内核都必须分配一个新的(通常 身体上 连续的;注意重点,相对而言,这是一个相当昂贵的操作)内存块,用于保存该内存空间区域的翻译映射。我将避免使用特定的术语,只需调用它们 L0 -> L1 -> L2 -> L3 具有 L0 是表示该虚拟内存空间的根映射。这将随页面大小的粒度和不同的操作系统或体系结构(例如Linux具有超级页面)而变化。

    现在,如果新的映射处于L3级别,并且有一个L3页表,那么新的映射将只涉及更改该区域中的一个条目来指示转换。如果没有L3页表,则必须分配新的L3页表,并将其输入到L2页表中。这样一直到l0页的表格。

    一些简要说明:

    • 每次更改映射时,通常都会有一个TLB(Translation Lookaside Buffer;MMU用于VM->物理转换的硬件缓存)无效惩罚(手动或自动)。
    • 有些页面可能不需要全部4个翻译阶段,翻译级别具有特定的大小,因此超级页面通常是一个页面,例如,使用二级页面表条目将该虚拟机空间的整个块映射到物理空间(这意味着只需要3个翻译级别)。
    • 各种体系结构使用不同的方法来减少TLB破坏的惩罚(即x86 U64上的PCID;事实上,一些熔毁缓解措施(如KPTI)会导致性能回归而没有它)。
    • 说到崩溃,一些内存范围可能有内核或蹦床映射或异常向量。这些是操作系统保留的。在64位系统的pre-spectre/meltdown中,内核通常会在每个页表中保持自己的映射。许多ARM处理器都有一种专用的机制,称为拆分页表(TTBR0/TTBR1;转换表基寄存器0/1)。
    • 上面的一个例子是linux vdso(虚拟动态共享对象),它是由内核创建的映射。与之相对应的darwin(osx/ios)是commpage(公共页)。这通常具有系统中每个进程共享的只读代码,并且具有当前时间(以降低系统调用的成本, gettimeofday 可以从vdso中读取,也可以使用vdso蹦床来读取)。
    • 当然,以上所有内容都因您所使用的体系结构、操作系统以及所使用的操作系统版本而异,因为虚拟内存管理器通常使用各种技术来确保不会出现碎片。 但是,如果您随机请求许多小的固定映射,是的,您将有效地绕过许多映射,从而导致性能问题。