使用更扁平模式的性能影响

Performance implications of using a flatter schema

我正在使用 FlatBuffers (C++) 来存储有关文件的元数据信息。这包括 EXIF、IPTC、GPS 和各种其他元数据值。

在我当前的模式中,我有一个相当 规范化的 定义,上面列出的每个组都有自己的 table。根 table 只包含每个子 table 的属性。

基本示例:

table GPSProperties {
  latitude:double;
  longitude:double;
}

table ContactProperties {
  name:string;
  email:string;
}

table EXIFProperties {
  camera:string;
  lens:string;
  gps:GPSProperties;
}

table IPTCProperties {
  city:string;
  country:string;
  contact:ContactProperties;
}

table Registry {
 exifProperties:EXIFProperties;
 iptcProperties:IPTCProperties;
}

root_type Registry;

这可行,但构建缓冲区时的嵌套限制开始使代码变得相当混乱。同样,将属性分解为单独的 tables 只是为了在模式中清晰。

我正在考虑 "flattening" 将整个架构合并为一个 table,但我想知道这样做是否对性能或内存有任何影响。这个 table 可以 有几百个字段,尽管大多数都是空的。

提案:

table Registry {
  exif_camera:string;
  exif_lens:string;
  exif_gps_latitude:double;
  exif_gps_longitude:double;
  iptc_city:string;
  iptc_country:string;
  iptc_contact_name:string;
  iptc_contact_email:string;
}

root_type Registry;

由于未设置或设置为其默认值的属性不会占用任何内存,因此我倾向于认为扁平化模式可能不是问题。但我不确定。

(请注意,性能是我最关心的问题,紧随其后的是内存使用。规范化模式表现出色,但我认为扁平化模式确实可以帮助我清理代码库。)

您应该首先了解的基础知识:

  1. 每个 table 的顶部都有一个 vtable,它告诉可以找到 table 的每个字段的偏移量。如果一个table中的字段太多,这个vtable会变大,不管你存不存。

  2. 如果您尝试创建 table 的层次结构,则您正在创建额外的 vtable,并且还会为设计增加间接成本。

  3. 如果多个对象中存储了相似的数据,则 vtable 也会共享。就像您创建的对象仅使用 exif_camera 变量一样!

因此,这取决于您的数据是否庞大且异构,是否使用更有条理的层次结构。但是,如果您的数据将是同质的,则更喜欢扁平化的 table.

由于您的大部分数据都是字符串,因此这两种设计的大小和速度将非常相似,因此您应该从软件工程的角度根据更适合您的方式进行选择。

也就是说,平面版本可能会在大小上更有效(更少 vtables)并且肯定会更快地访问(尽管再次强调,考虑到它主要是字符串数据,这是微不足道的).

平面版本可能效率较低的唯一方法是,如果您将大量它们存储在一个缓冲区中,其中设置的字段在每个 table 之间变化很大。那么非扁平化版本可能会产生更多的vtable分享。

在非平面版本中,table如 GPSProperties 可能是 struct 如果字段不太可能改变,这样会更有效率。

This single table could have a few hundred fields, though most would be empty.

性能成本可能很小,您不会注意到,但对我来说,您上面的引述是决定使用哪种设计的摇摆因素。

当其他人在谈论vtables的成本时;我根本不会担心那个。每个 class 有一个 vtable,每个 运行 准备一次并且不会很昂贵。 拥有 100 个空的和未使用的字符串将非常昂贵(内存使用明智)并且会耗尽您创建的每个对象;此外,阅读您的字段将变得更加复杂,因为您不能再假设您阅读的 class 的所有数据都在那里。

如果大多数/所有字段一直都在,那么我就可以看到做单的吸引力了class;但他们不是。