| RFC-0114:在 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;
}
InlinedValueEnvelope 和 OutOfLineWireEnvelope 这两种线路表示法都有重叠的 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 中使用的信封结构 的基础。