128-bit struct or 2 64-bit records for performance and readibility
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When people ask whether a 128-bit struct is better than two 64-bit values, they are usually mixing three separate concerns: memory layout, update semantics, and readability. The correct choice depends less on the number of bits and more on whether the data is truly one conceptual value or two independent ones.
Start with the Data Model
If the two halves always travel together and represent one thing, a single type is usually clearer. A hash, UUID-like value, or composite key is a good example. In that case, wrapping the two ulong fields in one small value type makes the API harder to misuse.
This design communicates intent. A method that accepts Hash128 is telling readers that the pair forms one unit.
If the two 64-bit values have separate meaning, keep them separate:
That is still one struct, but the fields now reflect distinct semantics. Splitting into two unrelated records only helps if they are genuinely used independently.
Performance Is Mostly About Access Patterns
For most business applications, the difference between one tiny struct and two small values is negligible. The bigger performance questions are:
- How often is the value copied.
- Whether it fits naturally in cache-friendly arrays.
- Whether methods pass it by value or by
in. - Whether the data is processed together or separately.
For example, if you store a large array of composite values and always use both halves at the same time, an array of structs is often a good layout:
If you frequently scan only one half, separate arrays may be better than either of the options in the article title:
That is a structure-of-arrays layout rather than an array-of-structures layout. It can reduce wasted memory traffic for some workloads.
The Atomicity Question
People often assume a 128-bit struct implies atomic 128-bit reads and writes. That is not a safe assumption in managed code. Unless the platform, runtime, and API explicitly guarantee atomic behavior for that exact operation, you should not design around it.
If multiple threads update related values together and correctness matters, use synchronization designed for that purpose. A single struct does not automatically make the update atomic.
Readability Usually Wins
Readability should not be treated as an afterthought here, because the performance difference is often tiny until profiling proves otherwise. A well-named type prevents bugs by making illegal combinations harder to express.
Compare these method signatures:
The second form is more self-explanatory. It also avoids parameter-order mistakes.
If you do choose a single struct, keep it simple. Prefer readonly struct or readonly record struct when the value should be immutable. That helps the type behave like a proper value object and makes intent obvious.
When Two Values Are Better
Separate values are better when operations naturally target them independently. For example, if one 64-bit field is a timestamp and the other is a sequence number, putting them in one opaque 128-bit abstraction may hide useful meaning.
In those cases, the problem is not "performance versus readability." The problem is whether the type boundary matches reality. A type that forces unrelated things together can make code harder to understand, even if the memory layout looks compact.
Common Pitfalls
- Assuming a
128-bit structautomatically gives atomic updates. It usually does not. - Optimizing for a theoretical layout win without measuring the real workload.
- Passing a small value type around in ways that cause unnecessary copying when
inwould be clearer. - Combining unrelated fields into one opaque type just because the total size is
128bits. - Ignoring naming. A well-named type often provides more value than a marginal layout tweak.
Summary
- Use one value type when the two
64-bithalves form one conceptual unit. - Keep separate values when the halves have distinct meaning or are used independently.
- Real performance depends more on access patterns and copying than on the raw bit count.
- Do not assume a
128-bit structgives atomic thread-safe updates. - In most applications, choose the model that communicates intent best and benchmark only if the code is hot.

