使用 Box 优化可选的已知长度数组的内存分配

sca*_*ala 15 rust serde

我有一个关于特定情况下的内存分配策略的问题。

我有大量的结构体通过 serde 来自 JSON API。JSON 响应中有一个特定字段,在少数情况下,该字段将存在并包含恰好包含 11 个元素的数组。在大多数情况下,该字段在响应中根本不存在。所有响应都需要存储。

大多数回复的格式如下:

{
  id: 1,
  event: "None"
}
Run Code Online (Sandbox Code Playgroud)

少数回复将采用以下格式:

{
  id: 2,
  event: "something",
  event_details : [11 json objects here]
}
Run Code Online (Sandbox Code Playgroud)

如果我将用于解析 JSON 的结构定义为:

#[derive(Deserialize, Debug)]
struct Event {
  id: u32,
  event: String,
  // EventDetail is a struct with an u32 and string field
  events: Option<[EventDetail; 11]>
 }
Run Code Online (Sandbox Code Playgroud)

我可以看到使用deepsizeof crateVec<Event>创建的文件占用了约 500MB 的内存。如果我将该events字段更改为,events: Option<Box<[EventDetail; 11]>>内存使用量会下降到约 150MB。

我的理解是,由于 Box 是在运行时而不是编译时分配的,因此当传入的 JSON 响应缺少该字段时events,则不需要分配 11 项数组。我在这里说得对吗?这是 Box 的有效用例还是有更好的方法来做到这一点?

我也尝试过events: Option<Vec<EventDetail>>,这导致内存分配比 Box 略高。我认为这取决于填充。

Sil*_*olo 15

是的,这都是正确的。Option<[T; N]>总是为数组分配空间,即使它没有被使用,而Option<Box<...>>为框指针分配足够的空间,但除非需要,否则为它指向的东西分配空间。将大数据隐藏在Box间接后面是 100% 正确的调用。

Vec是一个指针(基本上是 a Box)加上向量的大小及其容量,因此Option<Vec<...>>通常是开销的三倍,因为它将为(可能不存在的)向量的所有指针、大小和容量分配空间。但在这一点上,我们以字节为单位进行争论,因此在这种规模上争论大小差异是愚蠢的。

Vec还存储内存分配器,但默认分配器Global是零大小类型)

由于您提前知道大小,因此我会选择Option<Box<[EventDetail; 11]>>而不是向量,因为向量只是毫无意义地将数据推送到可以在编译时知道的运行时。

  • FWIW,您也可以只使用没有“Option”层的“Vec”。这应该与 `Option&lt;Box&gt;` 具有基本相同的开销(不过还要加上长度+容量),并且您可以使用 `#[serde(default)]` 将缺失值反序列化为空的 `Vec` (假设存在但为空的数组不是需要区分的不同状态)。 (3认同)