Skip to content

Holy's Reallocation Merging

Summary

Merge together consecutive reallocations of datatypes during ser/des to prevent multiple reallocations and allow special cases to skip reallocating logic in datatypes.

Motivation

One of the biggest performance costs when it comes to serializing our messages is reallocations. Pretty much every datatype we write to a buffer has some form of reallocation. But, when we use a lot of these datatypes consecutively and we know how big they are beforehand, we can squash those allocations together and write a version of that datatype without any of the spooky logic included for reallocating. Other programs like Blink, and Squash already implement this.

Design

Holy (and by extension, light) implements reallocation merging by storing the following "extra" data about its ser/des types:

local static_size: { [Shape]: number? }
local static_ser: { [Shape]: SerFunction }

local static_ser is an extra serialization function for types with a fixed size in bytes. The static_ser SerFunctions won't try to resize any buffers, they serialize their raw data with no extra logic. Essentially, calling a static_ser SerFunction internally means whoever is calling the function will take care of allocations. In practice, Holy is smart enough to dynamically create closures based on whether a generic input is static. Structs will merge all static fields first. If all fields of a struct are static, the struct itself becomes a static type with a size and static_ser SerFunction. Arrays, and Maps with Static Parameters will generate serde special-cases to allocate the product of the data length with the static size of each item.