本文档介绍了在将元素添加到向量时,计算大小的最佳方式,即以字节和句柄的形式衡量元素大小。这样做是为了尽可能增加一次可批量处理的元素数量,同时满足通道写入的内核上限。
总结
为了最大限度地提高通道的吞吐量,通常将大型响应作为多个事物向量进行批处理,例如使用分页 API。由于通道的上限为 64K 字节和 64 个句柄,因此有一个问题是可以在向量中批量处理多少个元素来使容量达到上限(而恰好低于字节大小和句柄计数阈值)。
以下各项的关键参考文档是 FIDL 有线格式规范。
下面列举了各种示例来说明实现分页功能的最佳方式:
蓝牙 WatchPeers
方法
以 fuchsia.bluetooth.sys.Access
协议的 WatchPeers 方法为例,其定义如下:
WatchPeers() -> (vector<Peer>:MAX updated, vector<bt.PeerId>:MAX removed);
首先,请求或响应前面有标头,即固定的 16 个字节或 sizeof(fidl_message_header_t)
(如此处所定义)。
每个矢量都有一个 16 字节的标头 sizeof(fidl_vector_t)
,后跟内容。
由于 bt.PeerId
是一个 struct{uint64}
(此处定义),因此它是一个固定的 8 个字节,因此 removed
矢量的内容为元素数量 * 8 个字节。
接下来,我们需要估算 Peer
的大小,它定义为表。表本质上是一个信封矢量,其中每个信封然后都指向字段内容。估算大小必须分两步完成:
- 确定所用的最大字段序数(也称为
max_set_ordinal
) - 确定每个现有字段的大小
Peer
的大小是表格标头(即 sizeof(fidl_table_t)
,16 个字节)加上最大的序数 * 信封标头(16 个字节,即 max_set_ordinal * sizeof(fidl_envelope_t)
)再加上内容(即每个当前字段的内容)的总大小。
字段相对容易调整大小,其中许多字段是其基元或封装容器,因此会产生 8 个字节(由于填充)。bt.Address
字段也是 8 个字节,因为它的定义会减少到 struct{uint8;
array<uint8>:6}
。string
字段是一个字节矢量(即 sizeof(fidl_vector_t) + len(name)
),并填充到最接近的 8 字节边界。
风景优美的 Enqueue
方法
以 fuchsia.scenic.Session
协议的 Enqueue 方法为例,其定义如下:
Enqueue(vector<Command>:MAX cmds);
请求或响应前面有一个标头,即固定的 16 个字节或来自 zircon/fidl.h 的 sizeof(fidl_message_header_t)
。然后,该矢量有一个 16 个字节的标头 sizeof(fidl_vector_t)
,接着是该矢量的内容,它们是实际命令。因此,在您考虑每个单独命令的大小之前,存在 32 个字节的固定大小。
命令是一个联合体,其标头为 24 个字节(即 sizeof(fidl_xunion_t)
),后跟内容,使内容对齐 8 个字节。
Command
union 内容的大小取决于所选的变体。此示例使用 fuchsia.ui.input.Command
类型的 input
变体。
(景观命令的)input
变体本身是一个联合,它会再添加一个 24 个字节的标头,后跟该联合的内容,例如 SendPointerInputCmd
类型的 send_pointer_input
。
下面提供了 SendPointerInputCmd
的简化定义以及通过此结构体可传递的所有类型:
type SendPointerInputCmd = struct {
compositor_id uint32;
pointer_event PointerEvent;
};
type PointerEvent = struct {
event_time uint64;
device_id uint32;
pointer_id uint32;
type PointerEventType;
phase PointerEventPhase;
x float32;
y float32;
radius_major float32;
radius_minor float32;
buttons uint32;
};
type PointerEventType = flexible enum {
// members elided
};
type PointerEventPhase = flexible enum {
// members elided
};
PointerEventType
和 PointerEventPhase
枚举默认是 uint32
的底层表示形式。您可以将 SendPointerInputCmd
的大小减小到结构体:
struct {
uint32; // 4 bytes, total 4
// 4 bytes (padding due to increase in alignment), total 8
uint64; // 8 bytes, total 16
uint32; // 4 bytes, total 20
uint32; // 4 bytes, total 24
uint32; // 4 bytes, total 28
uint32; // 4 bytes, total 32
float32; // 4 bytes, total 36
float32; // 4 bytes, total 40
float32; // 4 bytes, total 44
float32; // 4 bytes, total 48
uint32; // 4 bytes, total 52
};
因此,SendPointerInputCmd
结构体的大小为 52 个字节。如需详细了解如何计算结构体大小,请参阅《失落的结构包》。
现在,您已经调整了命令各个部分的大小,接下来将总大小相加:
fuchsia.ui.scenic.Command
的标头:24 个字节,即sizeof(fidl_xunion_t)
- 变体为
input
的内容:fuchsia.ui.input.Command
的标头:24 个字节,即sizeof(fidl_xunion_t)
- 变体为
set_hard_keyboard_delivery
的内容: - 结构体
SendPointerInputCmd
:52 字节 - 内边距调整为 8 个字节:4 个字节
这样产生的总大小为 104 个字节。