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

RFC-0114:在 FIDL 封装容器中内嵌小值
状态已接受
区域
  • FIDL
说明

此 RFC 提出了 FIDL 线格式更改,将大小小于等于 4 字节的值内嵌到封套正文中。

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

摘要

此 RFC 提出了 FIDL 线格式更改,将大小小于等于 4 字节的值内嵌到封套正文中。

设计初衷

进行此更改的目的是提高 FIDL 表和联合(即目前使用封套的布局)的性能。

FIDL 联合体和表使用称为封套的共享表示法来表示离线对象。线外指针是编码和解码开销的已知来源。小对象可以内嵌在封装容器中,从而避免了线下开销。

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

设计

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

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

  • bool
  • float32
  • uint8、uint16、uint32
  • int8、int16、int32
  • 布局为 uint8、uint16、uint32、int8、int16、int32 的枚举
  • 位,布局为 uint8、uint16、uint32、int8、int16、int32
  • handle、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 个字节的值必须使用非内嵌表示法。收到表示方式不正确的值时,必须触发解码错误。不存在的信封继续使用零信封表示法,这意味着它们始终由离线表示法表示。

实现

此更改需要进行复杂的迁移。不过,此迁移可以与其他线格格式迁移结合使用,在实践中会更经济。

性能

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

设置所有字段时的编码时间(以纳秒计):

# 字段 之前 之后
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 字节的嵌入值与高效封装容器不兼容。您需要做出选择,要么不使用高效封装容器,要么减小可内嵌的值的大小。本 RFC 选择了后者,因为这种方式似乎最有可能带来最显著的性能提升。

在先技术和参考文档

RFC-0113 引入了高效的信封,该信封构成了本 RFC 中所用信封结构的基础。