最大分页数

本文档介绍了在将元素添加到向量时,计算大小的最佳方式,即以字节和句柄的形式衡量元素大小。这样做是为了尽可能增加一次可批量处理的元素数量,同时满足通道写入的内核上限。

总结

为了最大限度地提高通道的吞吐量,通常将大型响应作为多个事物向量进行批处理,例如使用分页 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 的大小,它定义为表。表本质上是一个信封矢量,其中每个信封然后都指向字段内容。估算大小必须分两步完成:

  1. 确定所用的最大字段序数(也称为 max_set_ordinal
  2. 确定每个现有字段的大小

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.hsizeof(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
};

PointerEventTypePointerEventPhase 枚举默认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 个字节。