RFC-0032:高效信封

RFC-0032:高效封装容器
状态已拒绝
区域
  • FIDL
说明

此 FTP 为信封提出了更紧凑的编码。

作者
提交日期(年-月-日)2019-02-06
审核日期(年-月-日)2019-02-21

“将信封改造成明信片”

拒绝理由

此 RFC 最初于 2019 年 2 月 21 日获得接受。在 2019 年的大部分时间里,FIDL 团队致力于稳定线程格式,最终在第 3 季度和第 4 季度期间全力以赴。迁移已于 2019 年 12 月 1完成。

稳定性工作涵盖了多项变更:

不过,随着工作开展,12 月 1 日的截止日期迫在眉睫,FIDL 团队决定推迟实现高效信封更改,而将此工作推迟到 2020 年。与稳定性工作中涉及的其他更改不同,高效封装容器只是节省了内存大小,而且节省的空间非常小,尤其是与 FIDL 线格式(例如表的紧密格式)的其他方面相比。推迟是项目风险降低计算的结果,通过缩减范围,可以提高按时完成所有工作的几率。FIDL 团队的工作安排也是如此。

现在,距离推迟发布已经将近 18 个月,高效信封早已被人遗忘。2020 年开展的大量效果提升工作表明,此次变更不会产生实质性影响。

是时候面对现实了,这种情况不会发生。已拒绝。

与其他 RFC 的关系

我们于 2021 年 6 月重新审视了此主题,并使用目标基准衡量了性能。这是一个决定性的结果,RFC-0113 提议重新引入这项更改,该提议已被接受。

摘要

此 FTP 为封装容器提出了更紧凑的编码1

设计初衷

封装容器是可扩展、可演化的数据结构(表和可扩展联合体)的基础。封装容器的线格格式更紧凑、更高效,这使得这些可扩展结构可在性能和线格大小至关重要的更多情境中使用。

设计

建议的信封格式如下:

图:64 位小端字,MSB 32 位大小,16 位 handle_count,16 位预留

现有信封格式相比:

  • 大小字段保持不变(32 位)。
    • 大小包括可能递归编码的任何子对象的大小。
    • 例如,vector<string> 的大小包括外部矢量内部字符串子对象的大小。
    • 这与当前封装容器实现的 size 字段的现有行为一致。
  • 16 位为保留位。
    • 解码器必须验证预留位是否为零。
    • 如果我们日后想使用预留位,则应改为修改线格格式。
      • 应从更全面的角度考虑 FIDL 的预留位,以便行为在各个规范中保持一致。
      • 具体而言,FIDL 中没有先例表明解码器可以忽略任何位:线路上的所有位都已定义和指定。
      • 这是最简单的决定,即要求更改线格格式,而不是启用向前兼容性,以便在确定有关预留位数的政策之前保持简单。
  • handle_count 为 16 位,而不是 32 位。
    • 目前无法通过 Zircon 通道发送超过 64 个句柄;我们认为 16 位足以满足未来的需求。
    • handle_count 包含所有递归子对象的句柄数。
  • “存在/不存在”字段已被舍弃。
    • 存在性由 size 或 handle_count 字段中的非零值表示。
    • 不存在时,大小和手柄数字段均为零。
      • 我们将其称为“零封装容器”。
      • 零封装容器等同于 FIDL_ALLOC_ABSENT
  • 大小为 UINT32_MAX 且句柄数为 0 的情况比较特殊:它表示存在但大小为零的信封内容。
    • 这项功能预留用于在大小为零的空结构体成为现实2时使用,目前不会对解码器造成任何性能或复杂性开销。我们希望现在就提及这一点,以免日后可能的实现破坏了线格格式。
    • 我们可以改为盗用一个预留位。我们对此没有明确的意见;只要能够以某种方式将“存在但大小为零”的信封与 FIDL_ALLOC_ABSENT 区分开来,就没问题。很高兴能达成共识。

解码器可以使用指向封套数据的指针覆盖封套,前提是它们知道封套内容的静态类型(架构)。如需有关如何在内容类型不明的情况下处理信封的建议,请参阅未知数据部分。

编码/解码表单的 C/C++ 结构体

封信的编码形式可以由编码或解码形式的并集表示。

typedef union {
  struct {
    uint32_t size;
    uint16_t handle_count;
    uint16_t reserved;
  } encoded;
  void* data;
} fidl_envelope_t;

static_assert(sizeof(fidl_envelope_t) == sizeof(void*));

未知数据

接收器(验证器和解码器)在可演化数据结构中使用时,可能不知道封装容器的类型。如果接收器不知道类型,则可以最少地解析信封并跳过。

  • 封装容器的大小决定了要跳过的离线数据量。
  • 如果封装容器的句柄数不为零,验证程序必须处理指定数量的句柄。
    • 默认处理行为必须是关闭所有句柄。
  • 如果解码器希望就地解码,可以使用指向封套内容的指针覆盖未知封套。
    • 如果解码器确实使用指针覆盖了封装容器,则会丢失封装容器中的大小和句柄计数信息。绑定可以为解码器提供一种机制,以便在覆盖封装容器之前保存大小和句柄计数信息;此 FTP 不对此类机制的运作方式发表意见。

实施策略

此 FTP 是线格格式变更。

两个 FIDL 对等方都需要了解新封装容器格式,并将其理解传达给对等方,这样双方才能使用新格式。因此,这通常被视为硬转换。由于此 FTP 未添加任何新功能,如果我们决定将其作为硬转换进行发布,作者建议将此更改与其他线格格式更改(例如,提议的序数大小更改)分组。

不过,可以进行平滑过渡。 两种方法如下:

  1. 事务性消息标头中有一个 uint32 预留/标志字段。我们可以为发起方端预留 1 位,以指示它了解新线格格式,并分阶段进行软过渡:
    1. 确保所有客户端和服务器都能理解旧版和新版线格格式。我们将继续使用旧的线程格式。
    2. 通过让对等方在事务消息标头中设置位,启用新的线格格式。如果双方都设置了该位,则双方都可以切换到新线格格式。
    3. 在柔和过渡完成所有层的过渡后,Fuchsia 的所有部分都可以使用新线框格式。我们可以移除在事务性邮件标头中设置该位。
    4. 删除旧线格格式的代码,并取消预留事务消息标头位。
  2. 我们可以使用“[WireFormat=EnvelopeV2]”属性(或类似属性)修饰特定的 FIDL 消息类型和/或接口,以指明消息/接口应使用新的线格格式。
    1. 虽然使用 WireFormat 属性修饰接口似乎更适合进行线格格式更改,但对结构体实现 WireFormat 更改应该更容易,因为结构体可用于不同的接口,而绑定需要额外的逻辑来确定结构体的使用上下文。
    2. 我们建议接口 [WireFormat] 属性仅影响接口方法实参的线格格式,而不会递归地影响实参的结构体。
    3. 这样一来,团队就可以选择部分迁移并选择启用新线框格式,从而按照自己的节奏进行迁移。
    4. 所有结构体和接口都具有 [WireFormat] 属性后,我们就可以舍弃旧的线格格式,假定所有结构体和接口都使用新的线格格式,并忽略该属性。

这两种软过渡方法都需要大量的开发时间和测试时间,并且存在出错的可能性。实现代码以正确执行任一方法、按计划执行以及成功跟进以移除旧代码,需要付出大量努力。

我们可能会同时拥有用于处理旧版和新版线程格式的代码;否则,在我们实现对新线程格式的支持时,将无法逐步提交 CL。鉴于将存在用于处理这两种线程格式的代码,我们建议使用上述某种软过渡方法进行原型设计,以确定软过渡是否可行。此类原型设计工作还可能产生一些通用策略,以应对未来重大的线缆格式变更,这可能很有价值。 如果没有,那就只能接受c'est la vie(这就是生活);这是一个艰难的转变。

对于软过渡或硬过渡,Fuchsia 中手动创建 FIDL 消息的任何实例都需要升级到新的线格格式。

向后兼容性

建议的线格格式更改应与 API(来源)兼容。所有手动编写的 FIDL 代码都需要更新,才能处理新的线格格式。

线格格式更改与 ABI 不兼容。您或许可以通过实现策略部分中列出的策略实现 ABI 兼容性。

性能

这种 FTP 会显著缩减信封所需的大小,这似乎总体上会带来显著的净效益。不过,如果可扩展的数据结构因效率更高而变得更加普遍,那么与使用不可扩展的数据结构相比,这可能不利于总体消息的紧凑性,并且会导致更多动态分配。

工效学设计

  • 更高效的可扩展数据结构使其能够在效率至关重要的更多情境中使用,因此用户无需过多担心其性能,并且可以在之前需要使用不可扩展结构的地方获得可扩展性带来的好处。
  • 我们甚至可能建议默认使用表来存储 FIDL 数据结构,并将结构体保留用于高性能情境。
    • 可扩展的联合体 (RFC-0061) 已在尝试移除静态联合体。

文档

  • 线上传输格式文档需要更新。
  • 更新文档时,应将封套解释为一种一流的概念:这样,当读者遇到可选性和可扩展数据结构的线格格式时,便能更好地进行认知分块
  • 我们应更新 FIDL 样式指南,以便就何时应使用可扩展类型提供建议。

安全

此 FTP 不应对安全性产生重大影响。

一个小小的安全优势是,这种 FTP 会移除旧格式中大小和指针重复的信息。以前,您可能会收到大小/手柄不为零且 FIDL_ALLOC_ABSENT 的封装容器,或大小/手柄为零且 FIDL_ALLOC_PRESENT 的封装容器。这需要进行额外的验证检查,但现在已不再需要。

测试

  • 由于此 FTP 会更改信封的线格格式,因此我们认为现有的 FIDL 测试套件(尤其是兼容性测试)将能够充分测试使用信封的所有场景。
  • 如果我们同意以软过渡的方式实施线缆格式更改(请参阅实施策略部分),我们将添加测试,以便对等方进行协商,并可能改用新线缆格式。

缺点、替代方案和未知情况

如果我们认为此方案带来的效率提升不值得付出实现成本,则可以保留现有线程格式。

设计决策

虽然此 FTP 会提出建议,但我们也在积极征求有关以下决策的反馈和共识:

  • 我们是考虑软过渡还是硬过渡?如需了解优点和缺点,请参阅实现策略部分。
  • 我们建议使用 32 位大小、16 位句柄,并预留 16 位。
    • 32 位大小是否合理?
    • 使用 16 位句柄是否合理?
  • 此提案派生自 rfc-0026,该规范建议将数据直接内嵌到小于等于 32 位的封装容器中。
    • 我们决定从此提案中移除内嵌功能,因为它会显著增加实现复杂性,并且除非有大量可内嵌的字段,否则提供的益处微乎其微。
    • 我们正在努力从更全面的角度考虑可选性,例如将可选字段分组到单个可选结构体中。这可能会使内嵌可能带来的所有好处都失效。

在先技术和参考文档

此 FTP 是 rfc-0026 的简化版本,由于整个 FTP 未达成足够共识,因此被拒绝。

提案。 内嵌、所有封装容器以及将字符串/向量计数移出行都已移除。


  1. 此 FTP 基于 rfc-0026,但包含线下信封 

  2. 请注意,目前,空(零字段)结构体在线路上占用一个字节。