RFC-0219:Zircon 页面压缩

RFC-0219:Zircon 页面压缩
状态已接受
领域
  • 内核
说明

对匿名 VMO 的页面进行内核压缩。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2022-05-31
审核日期(年-月-日)2023-06-03

摘要

本文档建议添加一个内核内压缩和解压缩系统。 匿名用户记忆库

虽然该系统位于内核中,但它对用户空间并不完全透明 内存不足 100%。但除了 API 更改之外 更改会包含在内核中

设计初衷

Fuchsia 常用于资源受限的设备,这些设备 增加内存池将会极大地改善用户体验。

内存内核压缩是其他操作系统使用的标准技术 系统和支持将有助于 Fuchsia 实现对等。

利益相关方

教员

cpu@google.com

审核者

eieio@google.com、rashaeqbal@google.com、maniscalco@google.com

已咨询

mseaborn@google.com、mvanotti@google.com、wez@google.com、plabatut@google.com、 jamesr@google.com、davemoore@google.com

社交化

分享给 Zircon 的 RFC 前提案文档,其中包含内核设计和实现部分 内核团队和更广泛的利益相关方。

设计

本部分概述了实现的总体设计选择, 压缩策略以及建议的 API 更改和扩展。

老化和追踪

匿名网页的处理方式应与针对 保护年龄,这意味着:

  • 添加指向匿名网页的反向链接,以便 vm_page_tVmObjectPaged + 可执行偏移查询。这会增加需要更新 反向链接。
  • 更改网页队列,使其不处理支持分页器的网页和匿名网页 。

页面队列中的留存状态具有活跃和非活跃集合的概念。 为防止抖动,绝不会从活跃集中逐出现有逐出 条件。匿名网页将会加入相同的队列, 以及压缩方面的限制

压缩后的网页会从网页队列跟踪中移除,这类似于 被逐出的页面。压缩失败的网页将移至单独的列表, 不会再次尝试压缩。如果用户访问此类页面 移回活跃集、潜在存在时间并尝试进行压缩 。

虽然我们只会压缩无效网页,但会有三种触发器 支持:

  • 即使没有承受内存压力,对旧页面进行即时压缩, 处理 LRU 队列。
  • 在内存压力下压缩,与执行逐出操作一起执行。
  • 请压缩文件以避免 OOM。

这些触发器将通过内核 cmdline 标志进行切换。

VmPageList 标记

VmPageList 存储区需要一种额外的标记,该标记可以:

  • 表示页面存在,但被压缩了。
  • 存储足够的元数据,以便在压缩的存储空间中找到该网页。

VmPageList 的条目已经过编码以存储 vm_page_t 指针和零页面标记,并且经过扩展后可额外添加 编码。

目前,网页列表条目是一个 64 位的值,其编码为 选项:

  • 0:空白条目
  • 最低有效位 = 1:零页标记
  • 任何其他:指向 vm_page_t 的指针

此架构之所以有效,是因为指针对齐会让 vm_page_t 的底部 3 位 指针始终为零。因此,我们可以概括该架构,使 页面列表条目的底部 3 位表示类型,其余位 都是关联数据初始类型为:

  • 0:空条目或 vm_page_t 指针(具体取决于数据位是否已设置)
  • 1:零页面标记。剩余数据位必须为 0。
  • 2:压缩后的网页。剩余位是从 压缩系统
  • 3-7:目前的类型无效。

压缩操作

为了专注于简单高效的初始用例, 策略会执行单页压缩和解压缩, 按 VMO 对页面进行显式分组。

如果不按 VMO 对页面进行分组,系统会以相同的方式馈送压缩路径 与现有的逐出系统相同尽管来自不同 VMO 的网页 可有效混杂在单个存储系统中,这是一项严格的要求, 确保不会在不同 VMO 之间创建锁定依赖项。这是为了 防止信息通过计时渠道泄露, 具有高延迟的无关流程

为避免不必要的锁争用,执行压缩或 应在不持有 VMO 锁的情况下使用解压缩算法, 且持有解压缩所需的任何锁时,不应进行压缩。

压缩算法

下面是对压缩路径的简要说明,但有错误 处理支持请求已省略。压缩的输入是一个页面,即它拥有的 VMO 以及该页面在 VMO 中的偏移量。偏移用于验证 在获取 VMO 锁后,该页面仍在 VMO 中,该锁定在 否则将跳过此伪代码的错误处理。

Take VMO lock
Update page list to use temporary compression reference
Release VMO lock
Compress page to buffer
Take compressed storage lock
Allocate storage slot
Release storage lock
Copy buffer to storage
Take VMO lock
Check page list still holds temporary compression reference
Update page list to final compression reference
Release VMO lock

一开始使用临时引用,而不是立即使用引用的动机 为压缩文件提供更大的灵活性 为存储系统指定数据大小, 生成引用时要存储的内容。这使得它能够 参考文件中数据的确切位置,无需间接 再额外创建一个表

即使未使用临时引用,系统也会获取两次 VMO 锁定 以解决使用该网页时出现的任何潜在竞争问题。 进行压缩例如,该网页(本例中是参考资料) 可能会在压缩过程中被取消承诺, 除非我们再次获取 VMO 锁定并进行检查,否则就知道这一点。

在进行压缩时,由于未持有 VMO 锁,因此另一个线程 可能会找到并尝试使用临时引用。作为临时 引用实际上不包含可以解压缩的数据,我们有两个 解决此问题的方法:

  • 等待压缩完成,然后检索原始网页。
  • 创建原始页面的副本(系统会预先分配其内存) )。

这样做的目的是确保该网页归其所有,并且处于已知状态, 在整个压缩过程中发生的情况在压缩时使用原始网页 是个问题,因为:

  • 在压缩算法读取网页时修改网页会增加 压缩算法的潜在错误和受攻击面, 无法灵活应对并发数据更改。
  • VMO 可能更改其可缓存性或其他属性, 并行缓存使用量。

此处的建议是复制原始网页 这既能更快地解决请求,又能避免 还需要采用其他同步机制这里的复制操作是 但这与所有其他写入时复制一致 在 VMO 锁定下可能发生页面复制的路径。由于只有一个 每次执行单个压缩,任何给定的 VMO 操作都会达到 最多出现一次

解压缩算法

实现解压缩是为了利用现有的 PageRequest 系统, 已用于延迟分配和执行用户分页器 请求。与用户分页器 VMO 的缺失网页类似,系统也会生成 页面请求之后,VMO 页面列表中的压缩引用会触发 类似的流程:

Take VMO lock
Observe page is compressed
Fill in the PageRequest
Drop VMO lock
Wait on the PageRequest
Retry operation

在填充页面请求时,我们需要存储压缩引用 以及 VMO 和偏移量。这就需要 现有的 PageRequest 结构。

这与用户分页请求和压缩之间的区别在于, 等待可以直接处理请求,在这种情况下,这意味着执行 解压缩。

解压缩需要能够容忍多个线程尝试访问 同一页面,并提供一种实现优先级继承的方式:

Declare stack allocated OwnedWaitQueue
Take compressed storage lock
Validate compressed reference
if compressed page has wait queue then
    take thread_lock
    release compressed storage lock
    wait on referenced wait queue
else
    store reference to our allocated wait queue in compressed block
    release compressed storage lock
    decompress into new page
    take VMO lock
    update VMO book keeping
    release VMO lock
    take compressed storage lock
    take thread lock
    remove wait queue reference
    release compressed storage lock
    wake all threads on wait queue

此处,之所以使用 thread_lock,只是因为这是需要持有的锁 用于执行等待队列操作,且无需用于 thread_lock正在使用中。

目标压缩大小

由于与解压缩相关的成本,只值得将 网页的压缩版本。其他情况 也可以存储未压缩的版本,从而节省解压缩成本。

虽然 70% 这一目标很常见,但此目标规模应该是一个可调的选项。 其他系统中的应用

任何不符合此目标的压缩尝试都将被视为 已失败,并且该网页已重新置于网页队列中,如 “年龄和跟踪”部分中

零页扫描

由于压缩从根本上要求检查源路径中的所有字节 网页,从而有机会检测并删除重复的零网页。这个 表示另一种压缩成功结果,其中报告 输入页只是零页,不会将结果存储在 压缩存储系统VMO 调用方可以改为将页面替换为 零页面标记。

现成的压缩算法实现通常没有办法 来报告输入值全为零,因此要实现这一点,需要以下任一方法:

  • 修改或创建实现以执行此跟踪。
  • 将压缩结果与已知签名进行比较。

第二个选项利用压缩算法 是确定性的,通常在数十个网页中的 字节。如果先执行大小检查,然后进行内存比较, 所以价格足够便宜

此方案无需初始压缩实施即可执行 零页面检测,但必须构建相关 API 和 VMO 逻辑才能支持该功能。 最终,应替换现有的零页扫描程序,以解决此问题 资源。

压缩算法

建议将 LZ4 用作初始压缩算法。查看实验性版本 评估部分,但需要满足以下重要要求:

  • 非常适合小尺寸 (4KiB)。
  • 在稳定的后 init 执行期间不执行 malloc 或等效项 操作。
  • 可以在没有共享可变状态的情况下并行解压缩。压缩必须 能够与解压缩同时发生。
  • 尽管并非必需,但支持多个并行压缩本身。

LZ4 还有另外一个有用的属性,那就是能够取消压缩 根据受限的目标缓冲区空间提前请求内存用量。

压缩后的存储空间

压缩页面的存储是在代码复杂程度和运行作业之间的权衡取舍 可能出现碎片化和内存使用量增加的情况。首字母 将固定数量的压缩页面(在本例中为 3) 选择单个存储页面

此存储策略的核心概念是: 被视为具有左侧、中间和右侧存储槽。这个模型最多允许 压缩比为 3:1。

实施过程非常简单直接,涉及以下方面:

  • 在任何现有的部分填充的网页中搜索广告位。
  • 如果没有合适的广告位,则将其放置在新网页的左侧广告位中。

两个复杂问题分别是:

  • 部分填充的网页应根据其规模划分到各个搜索列表中 其空闲槽,通过 O(1) 搜索实现近乎最佳的打包。
  • 如果左侧或右侧槽位非常大,中间槽位可能会变得不可用 其中包含足够的数据。

此类策略的优势如下:

  • 轻松植入。
  • 可预测的操作,没有长尾性能。
  • 在最糟糕的情况下会降级,因为不使用任何额外的内存。

一种可能需要处理的病态行为是 因此可能存在可以填充的漏洞 要释放的存储空间。在以下情况下,可以实现这种压缩系统: 。

从长远来看,它可能会演变成或使用更通用的堆 Slab 样式分配器。

请参阅实验评估部分,了解一些初始结果。

会计和指标

虽然压缩操作在没有任何用户输入的情况下发生, 影响应如何报告内存用量。我们还想泄露 向用户空间提供有关压缩效果的额外信息。

内容与承诺

现有的查询接口会引用所提交的字节和页,以表示实际的 直接在 VMO 中分配以保存数据的内存。

这些现有查询包括:

  • ZX_INFO_TASK_STATS,用于报告 mem_private_bytes 中的已提交内存; mem_shared_bytes
  • ZX_INFO_PROCESS_MAPS,其中映射的 VMO 的 committed_pages 是 被举报。
  • 报告committed_bytesZX_INFO_PROCESS_VMOS / ZX_INFO_VMO
  • ZX_INFO_KMEM_STATS / ZX_INFO_KMEM_STATS_EXTENDED(报告vmo_bytes) 即被视为“已提交”

除了 投入。任何涉及承诺内存的查询将继续适用于 具体而言,是指在 VMO 中直接保存内容的内存。内容将 而是内核为用户保留的数据量, 并具体说明了具体的操作方式也就是说,内容可以直接存放在网页中,并且 因此也会计入提交次数,或被压缩,而不会计入 投入。

此比例的压缩内容被视为未知 存储空间大小尝试对存储空间中的各个压缩页面进行归因 这会很复杂且不稳定,因为存储空间大小 系统压缩行为以及由此造成的碎片化问题。

虽然匿名 VMO 从概念上开始为零,但这并不被视为 内核记住的内容。如果用户明确承诺或 对任何页面进行修改,使之成为内容。在此之后,即使用户离开 或将内容重置为零,内核没有义务注意到这一点, 可能会继续将其视为在跟踪的内容。

关于逐出和用户分页器,可从 用户分页器不会被视为内核跟踪的内容。因此, 同时用于减少承诺金额和内容数量

指标

了解压缩的性能,包括调优 / 开发和 为了持续监控系统运行状况,我们希望收集并提供相关指标。

这些指标应该能够解答关于压缩如何 以及它对系统性能的影响。具体而言,我们 想要了解以下信息:

  • 压缩存储比。
  • 压缩页面花费的时长。
  • 哪些类型的网页正在压缩以及哪些网页正在压缩。
  • 解压缩延迟时间。
  • 执行压缩和解压缩所花费的 CPU 时间。
  • 压缩尝试成功与失败的比率。

API 更改

通过结构体演进和查询版本控制进行扩展:

  • zx_info_maps_mapping_t 可包含 content_pages 字段。
  • zx_info_vmo_t 可包含 content_bytes 字段。
  • ZX_INFO_TASK_RUNTIME,将 page_fault_decompress_time 报告为 总共 page_fault 次。

以下 zx_object_get_info 查询需要版本控制:

  • ZX_INFO_PROCESS_MAPS
  • ZX_INFO_PROCESS_VMOS
  • ZX_INFO_VMO
  • ZX_INFO_TASK_RUNTIME

现有字段(例如 zx_info_vmo_t 中的 committed_bytes)将具有各自的 更新了注释和文档,明确指出它们不会统计 都处于压缩状态

在迭代阶段,确切内容尚不确定,但我们建议添加一个 要报告的 ZX_INFO_KMEM_STATS_COMPRESSION 个查询:

struct zx_info_kmem_stats_compression {
    // Size in bytes of the content that is current being compressed.
    uint64_t uncompressed_content_bytes;

    // Size in bytes of all memory, including metadata, fragmentation and other
    // overheads, of the compressed memory area. Note that due to base book
    // keeping overhead this could be non-zero, even when
    // |uncompressed_content_bytes| is zero.
    uint64_t compressed_storage_bytes;

    // Size in bytes of any fragmentation in the compressed memory area.
    uint64_t compressed_fragmentation_bytes;

    // Total amount of time compression has spent on a CPU across all threads.
    // Compression may happen in parallel and so this can increase faster than
    // wall clock time.
    zx_duration_t compression_time;

    // Total amount of time decompression has spent on a CPU across all threads.
    // Decompression may happen in parallel and so this can increase faster than
    // wall clock time.
    zx_duration_t decompression_time;

    // Total number of times compression has been done on a page, regardless of
    // whether the compressed result was ultimately retained.
    uint64_t total_page_compression_attempts;

    // How many of the total compression attempts were considered failed and
    // were not stored. An example reason for failure would be a page not being
    // compressed sufficiently to be considered worth storing.
    uint64_t failed_page_compression_attempts;

    // Number of times pages have been decompressed.
    uint64_t total_page_decompressions;

    // Number of times a page was removed from storage without needing to be
    // decompressed. An example that would cause this is a VMO being destroyed.
    uint64_t compressed_page_evictions;

    // How many pages compressed due to the page being inactive, but without
    // there being memory pressure.
    uint64_t eager_page_compressions;

    // How many pages compressed due to general memory pressure.
    uint64_t memory_pressure_page_compressions;

    // How many pages compressed due to attempting to avoid OOM or near OOM
    // scenarios.
    uint64_t critical_memory_page_compressions;

    // The nanoseconds in the base unit of time for
    // |pages_decompressed_within_log_time|.
    uint64_t pages_decompressed_unit_ns;

    // How long pages spent compressed before being decompressed, grouped in log
    // buckets. Pages that got evicted, and hence were not decompressed, are not
    // counted here. Buckets are in |pages_decompressed_unit_ns| and round up
    // such that:
    // 0: Pages decompressed in <1 unit
    // 1: Pages decompressed between 1 and 2 units
    // 2: Pages decompressed between 2 and 4 units
    // ...
    // 7: Pages decompressed between 64 and 128 units
    // How many pages are held compressed for longer than 128 units can be
    // inferred by subtracting from |total_page_decompressions|.
    uint64_t pages_decompressed_within_log_time[8];
};

报告压缩后页面花费的时间意味着时间戳必须 都记录下来这应该不会产生 对潜在压缩比的影响 网页被快速解压缩

ZX_INFO_KMEM_STATS_EXTENDED 分页器字节

ZX_INFO_KMEM_STATS_EXTENDED 查询包含专门用于 分页器支持的 VMO 中的字节数。具体包括:

    // The amount of memory committed to pager-backed VMOs.
    uint64_t vmo_pager_total_bytes;

    // The amount of memory committed to pager-backed VMOs, that has been most
    // recently accessed, and would not be eligible for eviction by the kernel
    // under memory pressure.
    uint64_t vmo_pager_newest_bytes;

    // The amount of memory committed to pager-backed VMOs, that has been least
    // recently accessed, and would be the first to be evicted by the kernel
    // under memory pressure.
    uint64_t vmo_pager_oldest_bytes;

提议的实现方案将统一由分页器支持的页面和匿名 VMO 页面 网页队列中,这样就无法提供此类信息。 因此,我们的建议是重新定义这些内容,以表示所有可分页 / 可回收的内容。 内存,而不仅仅是支持明确逐出的用户分页器支持的 VMO 的内存。 否则,系统将保留总计定义、最新定义和最早定义。

尽管回收可压缩内存不能直接转化为 增加的 PMM 可用内存,则这些查询并不代表 可回收的 PMM 内存的确切数量。而是提供数据洞见 内存的相对分布情况,可用于执行 在设备 OOM 时,oldest_bytes 等验证会下滑且接近于零, 等等

为用户分页器支持可逐出内存和可压缩内存的多个字段 匿名内存的价值也很可疑。代码始终 在这些情况下,能够区分它们是很有价值的,但大多数 我希望获得两者的汇总报告。

停用对压缩 / 延迟时间敏感的 VMO

对于延迟时间敏感型应用,需要有一种机制 对 VMO 停用压缩功能,无论解压缩延迟时间有多短, 这可能超出了他们的容忍范围

避免回收和其他类型的内核活动 内存访问延迟时间是一个当前需要解决的问题。如 因此,此处不会提出任何设计,但这项工作是 压缩。

扩展程序

除了这个初步提议的设计之外,我们还进行了一些探索和改进 可以执行的操作。虽然这些通常属于实施 在这里列出它们是为了激励 AI 的长期适宜性 设计。绝对应该进行的改进,最好在 有:

  • 移除单独的零页重复数据删除,以便进行压缩。

另外还有以下可选建议供您探索,可能对您有所帮助:

  • 先压缩分页器支持的内存,然后再执行逐出操作。
  • 支持在不舍弃未压缩页面的情况下执行压缩,直到 内存压力
  • 浏览其他旧网页列表,并尽快压缩网页,而不仅仅是 LRU 队列。

还可以进一步调整压缩存储空间和压缩算法, 要么直接进行演变,要么为不同产品提供选项。这些 可能需要单独的 RFC,具体取决于其侵入性或不同程度。

实现

实现流程将分为此处列出的多个阶段。

通用基架

更改常见的虚拟机系统以支持匿名的老化和跟踪 页面。这些 API 在行为或 API 方面没有任何变化, 风险最高的更改,先完成这些更改可以:

  • 在树中实施更改并经过最长时间的测试
  • 能够测量候选压缩池(即旧匿名 网页)。

内核实现

完成常见更改后,主内核实现便可进入 功能标志后面。这样就可以对 并实施压缩,以确保其稳定性,然后再公开其 通过任何 API 存在。

API 演变

执行 API 演变并为指标和会计设定结构迁移 更改。由于内核实现是在树中,因此可以更改 API 虽然压缩功能仍位于功能标志之后,但除此之外, 普通用户不会看到其他字段中的功能变化。

集成、扩展程序和调优

实施经过测试后,能够报告相关指标 以发现任何问题,允许任何扩展程序和微调 要执行的操作。

此测试的结果将确定:

  • 压缩功能默认处于开启状态,有些产品会停用压缩功能,或者
  • 压缩功能默认处于关闭状态,并且部分产品会选择启用压缩功能。

扩展和调整

扩展设计的某些部分现已可发布,常规实现 持续进行调优。

性能

有两个方面需要评估效果。

常见开销

即使停用了压缩功能,通用虚拟机代码也会发生根本性更改 需要进行哪些改进,从而对性能产生影响。

添加匿名网页的反向链接预计会增加可衡量的 所有 VMO 克隆创建和销毁事件的开销。这些操作包括 不在对性能要求苛刻的路径上,因为此类路径通常发生在初始化期间 和拆解步骤,但仍应检查对商品的任何影响。

其他 VMO 路径在维护反向链接和更新方面会产生少量开销 但这应该是在噪声中,且不会产生可衡量的影响。

现有的 VMO 微基准测试涵盖了这些路径,也就是 主要验证工具。

压缩性能

从本质上讲,压缩是要在 CPU 和内存之间进行权衡 只有在产品及其需求的基础上评估其效果。

否则,建议的指标 API 可让您了解 压缩的成本非常高,而压缩可调参数也可以用于 控制这些费用(与节省的内存量有关)。

安全注意事项

计时渠道

尽管压缩页面可能具有位于同一位置的存储空间, 必须定义访问此存储空间,以确保不存在传递依赖项 可能带来可衡量的渠道的 VMO。这样做是为了 避免与 内存重复信息删除攻击

尽管缺少传递依赖项,但计时渠道仍然 如果攻击者能够控制位于同一位置的数据, 与 Secret 相同的页面。这些数据经过精心设计 成功或失败后,攻击者可以学到一些东西 这个秘诀不过,目前还不清楚此类攻击是否确实可行。 防止压缩延迟时间的相同机制 敏感任务。

系统行为渠道

可以通过以下命令直接查询总体系统内存用量: ZX_INFO_KMEM_STATS_COMPRESSION),也可通过查询对象(如 VMO 并观察内容与提交的字节数之间是否存在差异。 您还可以查询“ZX_INFO_KMEM_STATS_COMPRESSION”中的其他统计信息。

这些信息提供了有关整体系统行为的间接信息。 然而,您可能并不认为,如果网站通过 VMO 访问的网页 并足以了解任何有关 过程。

LZ4

虽然 LZ4 库的文件数量和 LoC 指标较少, 使用低级 C 语言编写的代码仍然相当复杂,可能存在错误。通过 库将需要以下组合:

  • 针对整体适宜性的安全审核。
  • 额外的安全强化和测试。
  • 可以改写有问题的作品。

结合使用安全强化和重写操作可能会导致将部分或全部内容 实现更安全的表示法,例如 Rust 或 WUFFS。

测试

与效果类似,测试也有两个维度。

虚拟机正确性

常见虚拟机变更的技术正确性,以及 针对具体压缩的更改,将同时通过单元测试和 集成测试

对于无论压缩如何都会生效的常规虚拟机更改 或额外核心测试将作为 适当的选择。

使用 QEMU 运行程序进行的集成测试将用于通过 压缩。

系统行为

虽然压缩不是免费的,但它不应使任何行为回归 产品所有者认为至关重要的问题。这些行为是产品 具体示例包括音频跳过或加重的抖动 在内存不足的情况下用户体验不佳。

这将涉及利用锁定等工具手动测试产品 竞争跟踪,以及与产品测试团队和产品团队合作, 进行评估。

文档

zx_object_get_info 查询的新增和更改将记录在案。 以及其他 cmdline 选项。

缺点、替代方案和未知问题

未知情况和迭代

在确定此提案之前, 在真实产品上正确实施和评估。虽然此提案 努力为所有合作伙伴提供具体、合理的初始方案 算法和结构,但这些都几乎必定会随着时间推移而发生变化 真实使用情况数据

缺点

这种方案的主要缺点是增加虚拟机系统的复杂性, 由内核执行尽管压缩的任何性能方面始终 但要避免压缩,需要更改压缩包中的 通用基础设施因此,存在永久性的复杂性和风险 (即使未使用)。

用户分页器压缩替代方案

与在内核中执行压缩和解压缩相比, 通过用户分页器机制外包到用户空间。在很多方面 类似于 Linux 上的 ZRAM,其中由用户空间文件系统实现 遗憾的是,它也有很多相同的缺点。

优势

在用户空间中进行压缩主要有两个原因:

  1. 从内核中移除了复杂的压缩和解压缩代码。
  2. 让用户可以灵活实现。

遗憾的是,(1) 不太真实,除非有单独的封闭实例 会为每个安全域创建 (但具体数量会确定), 此用户空间进程可以查看系统中每个进程的匿名内存 系统。这会使其变得非常可信,因为任何入侵都可能 查看和修改系统中的任何用户内存。因此,信任 与内核相比,这里的压缩和解压缩代码要低一些。

灵活实施当然是值得的,不过 如果有一种机制来分配 匿名 VMO 连接到特定压缩器。那么,您可能并不需要 但可以实施交换或任何其他策略。

缺点

用户空间压缩的主要缺点是它对 由内核执行能够乐观地压缩和解压缩网页 (可能甚至不会舍弃未压缩的文件)让内核 灵活性高所有这些架构都可以通过 但它会成为更复杂的分布式系统问题, 需要非常仔细的 API 和并发推理。

尽管 Zircon 是一个组件系统,但是切换用户所需的成本很低 空间任务,需要向下调用用户空间进程以解压缩 4KiB 网页会显著增加延迟时间和费用。否则可能会导致延迟 压缩,直到页面创建更旧,从而减少潜在的内存节省机会。对于 则假定缓慢持续 系统正在查询存储,因此逐出已经倾向于延迟逐出 尽可能远

即使压缩和解压缩操作由用户空间处理, 仍然需要对网页存在时间跟踪和压缩触发 完成。因此,所有可调参数和指标仍然需要存在于内核中。 此外,虚拟机系统目前无法将用户分页器与 子 VMO,因此需要进一步更改和重新设计虚拟机 系统。

内核代码解释器

我们可以支持用户提供的 通过用户提供的内核代码(即 la Linux eBPF)运行该算法。这样, 用户实施方案非常灵活,而且不会造成 降低用户通话质量

虽然不需要显式模式转换,但内核仍然需要 将这种情况视为类似于用户致电中断,因为性能特征 是未知的,因此仍然需要注意锁定和其他 依赖项。

这种方法的主要缺点是,内核中 需要足够的功率才能产生可接受的压缩性能 进行大规模实施,当然,这必然很复杂, 内核的可信计算基础。

未来

将来,我们可能需要一种方式让匿名 VMO 支持交换 storage。这绝对需要通过 分页器机制。此时,这种交换实现可以进行压缩, 而不仅仅是进行交换。不过,由于 我认为这与内核压缩并存, 。

会计替代方案

此提案目前建议对内容字节/网页使用新定义 来补充现有的承诺数量在不改变统计方式的情况下 还有其他一些命名选项,例如:

  • 并致力于
  • 居民和内容

以上每种情况都需要更改承诺的所有现有使用方式, 规模变化,并在两个 API 版本之间造成更大的不连续性。

ZX_INFO_KMEM_STATS_EXTENDED 分页器字节备用选项

虽然建议的实现统一了分页器支持的和匿名 因此它们的计数无法轻松分离, 实现可以分开计数。

此替代实现方案将针对每个事件具有两组不同的计数器 可回收队列的大小page_queue_priv 中的高位,存储在 vm_page_t 将用于区分在以下情况下需要更新 在队列之间移动页面

这种方法的缺点是:

  • 增加了 page_queue_priv 使用方式的复杂性。现在它变成了 字段而不是计数器,并且所有读写操作都需要 额外数据。
  • 进入收割,手持拱门时可进行收割,成本 随着需要解码队列信息和正确计数而增加 已更新。
  • 多个计数字段,并增加了保持它们一致的复杂性。

正如提案中所述,我认为分页器字节字段实际上 这样做的好处是,在给定次数的情况下, 这会增加实现复杂性和运行时成本。

实验评估

为了提出 LZ4 压缩算法和压缩后的存储空间 已构建了一个实验性版本的压缩功能, 以便:

  • 收集被认定为不活跃的匿名网页的示例数据集, 压缩。
  • 对此数据集运行不同的压缩算法。

其目的是通过下列方式评估不同的压缩算法:

  • 来自实际产品的真实输入数据。
  • 在与内核进行正确匹配的受限执行环境中。

第二点尤为重要,因为内核代码在没有支持的情况下运行 用于 FPU/SSE/AVX 等

评估使用的是 64MiB 的输入数据集,每个 4KiB 页面都是 像在实际运行中一样,单独馈送给压缩机。通过 结果数据的解释如下:

  • 成功压缩意味着至少压缩到原始大小的 70%。
  • 系统会统计所有网页(包括最终压缩后压缩的网页) 不会压缩存储的这与未知的实际使用相符 无论压缩成功与否
  • 只有已成功成功完成解压缩的网页 压缩。
  • 总大小会计入每个页面所需的存储空间,因此之前存储的页面数 将其压缩后的大小加到计数中,以及 压缩失败的网页计为 4KiB。

采用这种计数方式的理由是,最终目标是释放 并使用最少的 CPU。一种压缩算法,有时 网页会大幅压缩网页,否则会耗费大量 CPU 资源 压缩实际上并不会节省太多内存,也不会占用大量 CPU。

北卡罗来纳大学 7 号

算法 压缩 (MiB/s) 解压缩(MiB/秒) 解压缩最差延迟时间 (ns) 总大小 (MiB)
Zstd(-7) 752.1470396 1446.748532 9302.3125 18.61393929
Minilzo 1212.400326 2135.49407 6817.839844 16.16410065
lz4(1) 1389.675715 2920.094092 6355.849609 17.21983242
lz4(11) 1801.55426 3136.318174 5255.708984 19.92521095
WKdm 1986.560443 3119.523406 3095.283203 21.98047638

ARM A53

算法 压缩 (MiB/s) 解压缩(MiB/秒) 解压缩最差延迟时间 (ns) 总大小 (MiB)
Zstd(-7) 189.3189527 486.6744277 29856.93359 18.61393929
Minilzo 317.4546381 528.877112 15633.99219 16.16410065
lz4(1) 294.5410547 1127.360347 12683.55273 17.21983242
lz4(11) 378.2672263 1247.749072 12452.67676 19.92521095
WKdm 337.6087322 471.9796684 14254.39453 21.98047638

ARM A73

算法 压缩 (MiB/s) 解压缩(MiB/秒) 解压缩最差延迟时间 (ns) 总大小 (MiB)
Zstd(-7) 284.3621221 623.15235 20958.90332 18.61393929
Minilzo 487.4546545 747.9218419 16784.83105 16.16410065
lz4(1) 441.5927551 1159.839867 10007.28418 17.21983242
lz4(11) 573.7741984 1240.964187 9987.875 19.92521095
WKdm 639.5418085 597.6976995 13867.47266 21.98047638

压缩后的存储空间

使用同一 64MiB 测试数据集评估了提议的压缩率 存储系统在本示例中,它以两种模式进行评估:双页系统 仅有一个左槽和一个右槽位,以及提议的三槽位系统。 这样做的目的是尝试量化引入新技术 真正简单的双槽系统之上的中间槽位

建议的压缩算法 LZ4 用于生成 压缩数据,加速度因数设置为 11。这意味着 对于 16384 个输入页面,12510 会生成需要存储的数据, 3874 项未压缩。

老虎机 存储空间页面 总 MiB 节省 MiB 压缩比
双槽 6255 39.5 24.4 1.62
3 槽 4276 31.8 32.1 2.01

请注意,6255 恰好是 12510 的一半,表示有朋友 每个网页都有可用的压缩比,也就是说, LZ4 输入,这是此存储策略的最佳结果。

先验技术和参考资料

所有主流的现代操作系统(Windows、MacOS、Linux 和 Linux) CastOS 等衍生产品支持内存压缩, 对磁盘交换的补充。

三页存储策略的灵感来自 Linux zbud 和 z3fold 内存 管理系统