RFC-0114:在 FIDL 信封中内嵌较小的值 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 此 RFC 提出了一项 FIDL 传输格式更改建议,将大小小于或等于 4 个字节的值内嵌到信封正文中。 |
Gerrit 更改 | |
作者 | |
审核人 |
|
提交日期(年-月-日) | 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;
}
InlinedValueEnvelope
和 OutOfLineWireEnvelope
的线上表示形式都有重叠的 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 中使用的信封结构的基础。