RFC-0032:高效信封

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

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

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

“将信封变成明信片”

拒绝理由

2019 年 2 月 21 日,此 RFC 最初被接受。FIDL 团队在 2019 年的大部分时间里都在努力稳定线格式,最终在第 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> 的大小包括外部向量的内部字符串子对象的大小。
    • 这与当前信封实现的大小字段的现有行为相匹配。
  • 16 位为保留位。
    • 解码器必须验证预留位是否为零。
    • 如果我们希望在未来使用预留位,则应修改线格式。
      • 应更全面地考虑 FIDL 的预留位,以便在不同规范中保持一致的行为。
      • 具体而言,FIDL 中没有先例表明解码器可以忽略任何位:线路上的所有位都是已定义和指定的。
      • 此决定是最简单的决定,即要求更改有线格式,而不是启用向前兼容性,以便在确定有关预留位的政策之前保持简单。
  • handle_count 为 16 位,而不是 32 位。
    • 目前无法通过 Zircon 渠道发送超过 64 个句柄;我们认为 16 位足以满足未来的需求。
    • handle_count 包括所有递归子对象的句柄数。
  • 已舍弃存在/缺失字段。
    • 如果 size 或 handle_count 字段中的值为非零值,则表示存在。
    • 如果大小和句柄数字段均为零,则表示不存在。
  • 大小为 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 属性修饰接口似乎更符合线格式更改,但在线格式更改中实现结构体应该更容易,因为结构体可以在不同的接口中使用,并且绑定需要额外的逻辑来确定使用结构体的上下文。
    2. 我们建议,接口的 [WireFormat] 属性仅影响接口方法实参的线格式,而不以递归方式影响实参的结构。
    3. 这样一来,团队可以部分迁移并选择启用新的有线格式,还可以按照自己的节奏进行迁移。
    4. 一旦所有结构和接口都具有 [WireFormat] 属性,我们就可以舍弃旧的序列化格式,假设所有结构和接口都使用新的序列化格式,并忽略该属性。

这两种软过渡方法都需要大量的开发时间、测试时间,并且容易出错。 正确实现代码以采用任一方法、执行计划并成功跟进以移除旧代码是一项艰巨的任务。

我们很可能会同时使用代码来处理旧线格式和新线格式;否则,在实现对新线格式的支持时,就无法逐步提交 CL。鉴于处理这两种序列化格式的代码将存在,我们建议使用上述软过渡方法之一来测试软过渡是否可行。此类原型设计工作还可能带来有关如何顺利实现未来重大线框格式变更的通用策略,这可能很有价值。 如果不是,那就只能顺其自然,进行硬过渡。

无论是软过渡还是硬过渡,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(本提案源自该 RFC)建议将数据直接内嵌到信封中,以用于 <= 32 位的数据类型。
    • 我们决定从该提案中撤回内联,因为这会增加显著的实现复杂性,并且除非有大量可内联的字段,否则只能提供边际效益。
    • 我们正在努力更全面地考虑可选性,例如将可选字段分组到单个可选结构体中。 此类工作可能会使内嵌带来的任何好处过时。

在先技术和参考资料

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

提案。 内联、随处可见的信封以及将字符串/向量计数移出内联,这些都已移除。


  1. 此 FTP 基于 rfc-0026,但包含带外信封 

  2. 请注意,目前,空(零字段)结构在网络上传输时占用一个字节。