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

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

此 RFC 提议更改 FIDL 线路格式,将大小 <= 4 字节的值内嵌到信封正文中。

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

摘要

此 RFC 提议更改 FIDL 线路格式,将大小 <= 4 字节的值内嵌到信封正文中。

设计初衷

此更改的设计初衷是提高 FIDL 表和联合(即目前使用信封的布局)的性能。

FIDL 联合和表使用共享表示法来表示带外对象,这种表示法称为信封。带外指针是编码和解码开销的一个已知来源。小对象可以内嵌在信封本身中,从而避免带外开销。

此外,在某些情况下,还可以减少分配。对象可以直接存储在信封中,而不是为对象分配带外位置并从信封指向该位置。

设计

此 RFC 设计假定 RFC-0113(引入了高效信封)已获批准。

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

  • 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 中使用的信封结构 的基础。