Alignment

The alignment of a type restricts where it can be located in memory to optimize hardware loads and stores. Because rkyv creates references to values located in your serialized bytes, it has to ensure that the references it creates are properly aligned for the type.

In order to perform arithmetic and logical operations on data, modern CPUs need to load that data from memory into its registers. However, there's usually a hardware limitation on how the CPU can access that data: it can only access data starting at word boundaries. These words are the natural size for the CPU to work with; the word size is 4 bytes for 32-bit machines and 8 bytes for 64-bit machines. Imagine we had some data laid out like this:

0   4   8   C
AAAABBBBCCCCDDDD

On a 32-bit CPU, accesses could occur at any address that's a multiple of 4 bytes. For example, one could access A by loading 4 bytes from address 0, B by loading 4 bytes from address 4, and so on. This works great because our data is aligned to word boundaries. Unaligned data can throw a wrench in that:

0   4   8   C
..AAAABBBBCCCC

Now if we want to load A into memory, we have to:

  1. Load 4 bytes from address 0
  2. Throw away the first two bytes
  3. Load 4 bytes from address 4
  4. Throw away the last two bytes
  5. Combine our four bytes together

That forces us to do twice as many loads and perform some correction logic. That can have a real impact on our performance across the board, so we require all of our data to be properly aligned.

rkyv provides two main utilities for aligning byte buffers:

Both of these types align the bytes inside to 16-byte boundaries. This should be enough for almost all use cases, but if your particular situation requires even higher alignment then you may need to manually align your bytes.

In practice

rkyv has a very basic unaligned data check built in that may not catch every case. If you also validate your data, then it will always make sure that your data is properly aligned.

Common pitfalls

In some cases, your archived data may be prefixed by some extra data like the length of the buffer. If this extra data misaligns the following data, then the buffer will have to have the prefixing data removed before accessing it.

In other cases, your archived data may not be tight to the end of the buffer. Functions like archived_root rely on the end of the buffer being tight to the end of the data, and may miscalculate the positions of the contained values if it is not.