perf(proto): reduce Value's size by one-word #522
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In general, I value simplicity over cleverness. However, in this case, I trade some simplicity for a bit of cleverness to gain a considerable performance boost. Here is my justification:
In the previous implementation, we need one more 1-byte field,
typ
, just for storing a type, resulting one-word penalty for every singleValue
we have. In 64-bit machine, one-word is 8 bytes, so the size of theValue
will be 8 + 8 + (1 + 7 padding byte) = 24 bytes. If we use this SDK to handle a really large fit files, such as from a ultra-long-run or ultra-triathlon race, the one-word penalty will be significantly multiplied as we have way more messages. And imagine when this SDK is used to handle multiple fit files at once.We only have 25 types for Value (extremely unlikely that we ever need to add more types), the nearest bits to hold such value is 5 bits (can hold up to 31 number). It's not even 1 byte (8 bits) yet we "waste" 8 bytes for it due to padding. So I'm thinking if we can squeeze the type identifier into
num
field instead for a slice and use pointer as an identifier for a numeric value, so we can reduce Value's size from 24 bytes to 16 bytes in 64-bit machine.I take a measurement from two perspectives to ensure that the changes will be reasonable and safe:
From safety perspective, there are two things to consider:
panic: runtime error: makeslice: len out of range
). So even if people try to use Value to hold more than 255 bytes value, this can still hold huge number of items before the compiler will stop them from doing it. So again, reducing 1 byte fromnum
field is okay.From code complexity perspective, the change is considerably minimal, only some bits manipulation over here and there. We use bits manipulation everywhere anyway since we handle encoding.
The result of the new Value struct with a small additional change:
You might notice that now comparing two value is allowed. My justification is having two same values pointing to the same pointer should be an identical value.
Benchmark
The goal of this change is to enhance performance with the trade-off being more complex than before, so without a significant gain I wouldn't open this PR.
We have fair amount of improvement, especially in memory usage. It seems like there is +6% CPU overhead for encoding protocol messages, however, it doesn't take a consideration of the creation of messages and holding it in memory, so this is actually negligible.