| RFC-0114:在 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;
}
两种有线表示法 InlinedValueEnvelope 和 OutOfLineWireEnvelope 都具有重叠的 flags 字段。flags 中的最低有效位用于指明是否使用内嵌形式:1 表示内嵌,0 表示非内嵌。所有未使用的标志位都必须为 0。
FIDL 中只有一种规范的数据表示形式。大小不超过 4 字节的当前值必须内联,而超过 4 字节的值必须使用非内联表示法。如果收到表示形式不正确的值,必须触发解码错误。 缺少的信封继续使用零信封表示法,这意味着它们始终由带外表示法表示。
实现
此更改需要进行复杂的迁移。不过,此迁移可与其他线格式迁移相结合,从而在实践中大大降低成本。
性能
当字段内嵌时,LLCPP 中的编码时间显著减少 (CL):
对设置了所有字段的时间(以纳秒为单位)进行编码:
| # 字段 | 之前 | 添加动画后 |
|---|---|---|
| 1 | 178 ns | 147 纳秒 |
| 16 | 720 ns | 325 ns |
| 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 中使用的信封结构奠定了基础。