RFC-0114:将较小的值内联到 FIDL 信封中

RFC-0114:在 FIDL 信封中内嵌较小的值
状态已接受
领域
  • FIDL
说明

此 RFC 提出了一项 FIDL 传输格式更改建议,将大小小于或等于 4 个字节的值内嵌到信封正文中。

Gerrit 更改
  • 545282
作者
审核人
提交日期(年-月-日)2021 年 6 月 24 日
审核日期(年-月-日)2021-07-21

总结

此 RFC 提出了一项 FIDL 传输格式更改建议,将大小小于或等于 4 个字节的值内嵌到信封正文中。

设计初衷

我们之所以做出这项更改,是为了提升 FIDL 表和联合(即目前使用封装的布局)的性能。

FIDL 联合和表使用外行对象的共享表示法(称为信封)。外行指针是已知的编码和解码开销来源。小对象可以内嵌到信封本身中,避免了外行开销。

此外,在某些情况下,您或许可以减少分配金额。您可以直接将对象存储在信封中,而不必为对象分配外行位置并从信封指向该对象。

设计

此 RFC 设计假定 RFC-0113 获得了批准,其中引入了高效的信封。

以下类型将使用新的内嵌值格式:

  • bool
  • 浮点数 32
  • uint8、uint16、uint32
  • int8、int16、int32
  • 具有布局 uint8、uint16、uint32、int8、int16、int32 的枚举
  • 具有布局 uint8、uint16、uint32、int8、int16、int32 的位
  • 句柄、client_end、server_end
  • 结构体大小小于等于 4 个字节
  • 数组大小 <= 4 个字节

如果未来添加了大小小于 4 个字节的新类型值,那么除非另有说明,否则它们也将使用内嵌值格式。

新格式可以通过 C 结构体表示法来描述:

// An envelope corresponds to a union value or an entry in a table.
struct Envelope {
    union {
        // Inlined values have the same envelope structure for both wire and
        // decoded formats.
        InlinedValueEnvelope inlined_value_envelope;

        // Out-of-line values have a different structure on the wire and in
        // decoded format.
        union OutOfLineEnvelope {
            // Wire representation.
            OutOfLineWireEnvelope out_of_line_wire_envelope;
            // Decoded representation.
            void* decoded_data;
        };
    };
};
struct InlinedValueEnvelope {
    // A <= 4-byte value stored little-endian and 0-padded up to 4-bytes.
    uint8_t value[4];
    // Number of handles within the envelope.
    uint16_t num_handles;
    // Bit 0 of flags is 1 to indicate the inline representation is used and
    // the envelope is present.
    uint16_t flags;
};
struct OutOfLineWireEnvelope {
    // Number of bytes recursively within the envelope.
    uint32_t num_bytes;
    // Number of handles recursively within the envelope.
    uint16_t num_handles;
    // Bit 0 of flags is 0 to indicate the out-of-line representation is used.
    uint16_t flags;
}

InlinedValueEnvelopeOutOfLineWireEnvelope 的线上表示形式都有重叠的 flags 字段。flags 中的 LSB 指示是否使用内联表单:1 表示内联,0 表示外联。所有未使用的标志位都必须为 0

FIDL 中的数据只有一种规范化表示形式。必须内嵌大小不超过 4 个字节的存在值,且超过 4 个字节的值必须使用外联表示法。收到错误表示形式的值必须触发解码错误。 缺失的信封继续使用零包封表示法,这意味着它们始终由外行表示法表示。

实现

这项更改需要进行复杂的迁移。但是,此迁移可以与其他传输格式迁移相结合,从而降低实际成本。

性能

当字段内嵌 (CL) 时,LLPP 中的编码时间会显著缩短:

使用以下所有字段设置时间对时间(以纳秒为单位)进行编码:

# 字段 之前 之后
1 178 纳秒 147 纳秒
16 720 纳秒 325 纳秒
256 9396 纳秒 2909 纳秒

此图表显示了编码时间与表所含字段数的函数。表中的所有字段均已设置。

系统未测量解码时间,但由于解码算法遵循与编码类似的一系列步骤,因此解码时间也会得到显著改善。

此外,在某些情况下,绑定或许能够避免针对较小的值进行分配,这将进一步提高性能。

工效学设计

此 RFC 允许绑定避免为较小的值进行分配,但并未规定必须这样做。如果绑定发生更改,则用于处理这些类型的 API 最终可能不同于处理其他需要分配的类型的 API。这种不一致性可能会导致人体工程学设计不佳,必须格外小心,避免这种情况的发生。

向后兼容性

此变更所需的迁移会破坏 ABI 兼容性。

不过,更改生效后,类型更改的 ABI 兼容性不会受到任何影响。对于在此变更之前和之后进行的类型变更,所有小于或等于 4 字节的类型均无法保证 ABI 兼容性。

此变更可能会破坏源代码兼容性。RFC 不需要对源代码兼容性做出破坏性更改,但如果绑定可以提高绑定性能或其他原因,则可以选择做出破坏源代码兼容性的更改。

安全注意事项

这不会影响安全性。

隐私注意事项

这不会对隐私权造成任何影响。

测试

系统将使用几种策略来测试变更:

  • 每个绑定中的自定义单元测试。
  • GIDL 一致性套件。
  • FIDL 兼容性测试。

文档

线上格式文档需要更新。

性能权衡需要记录在 API 评分准则中,以便为字段大小决策提供参考。

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

缺点

这种方案的主要缺点是复杂性更高。现在,值有两种表示形式:内嵌和外行,具体取决于类型,切换行为有 4 字节的阈值可能令人惊讶。

替代方案:8 字节内联值

此 RFC 建议内嵌值不超过 4 个字节。这是因为,内嵌 8 字节值似乎不可行 - 至少在与高效的信封结合使用时,这种实现方式似乎不可行。

这是因为绑定必须支持未知的信封。收到未知信包时,不知道类型信息。因此,不知道它是否指向外接对象,这将改变解码器的行为。因此,值本身中需要一些信息来指示该值是采用内嵌格式还是外行格式。

如果信包大小为 8 个字节,并且内联的值是 8 个字节,那么当值是内嵌或外行格式时,就没有可存储的空位。

因此,8 字节的内联值与高效信封不兼容。需要做出相应选择:要么不使用高效的 envelop,要么减小可内嵌的值的大小。此 RFC 则选择后者,因为此方向似乎最有可能带来最显著的性能提升。

早期技术和参考资料

RFC-0113 引入了高效的信封,它们构成了此 RFC 中使用的信封结构的基础。