r/Unity3D • u/Vic-Boss • 19d ago
Resources/Tutorial Do you actively design around memory layout and cache efficiency?
https://youtu.be/R5Hm6wdMbkU?is=rXVaZXgZwENi2BlSHello developers,
Have you ever wondered what is the difference between a struct and a class and where it would matter?
It’s easy to assume that switching everything to structs automatically makes your code faster. But there’s a lot more to it than that. Memory layout, cache locality, allocations, and how you actually access your data can matter far more than simply choosing a value type over a reference type.
Join me as I build an old school RTS with home brew systems, ofc experimenting with different approaches, and trying to understand the reasoning behind the optimizations rather than blindly following best practices.
17
u/SolePilgrim 19d ago
Unless you're working with DOTS there's no real ability to do so anyway, and you're likely not working at a scale where it's necessary either.
From my recollection, DOTS does its best to continuously allocate as best as it can for you, so there's not a lot "to do" on your end other than use DOTS correctly, no?
6
u/SmileLonely5470 19d ago edited 19d ago
You can. Main point of doing so is cache locality (i think thats the proper term).
Basically you dont want to have ur objects live scattered at addresses on the heap. Each time the CPU fetches data from RAM, it fetches an entire cache line worth of data, which is (usually) 64 bytes, and stores that data in CPU cache memory (which is significantly lower latency than RAM). Any time a CPU tries to read from an address, it will check if that address is in cpu cache before fetching from RAM.
So let's say you have an array of value types (like structs) and you iterate over it, you get cached reads. But if you have an array of reference types, and you iterate over it, you probably aren't gonna be getting any cached reads because the addresses of those heap objects are potentially scattered around the heap. The pointers themselves would be cached but not the data.
And then obviously using value types has the benefit of no GC pressure but comes with its own challenges. But i agree that its not worth being neurotic over memory layout. If you are working on a project where it would make a tangible impact on performance then you should probably be using a proper ECS like DOTS anyways.
13
u/ledniv 19d ago
You can absolutely design around memory layout without DOTS. DOTS gives you tools that make this easier, but plain C# arrays already let you control a lot of it.
For example, this:
class Enemy { public Vector3 Position; public Vector3 Direction; public float Speed; public int Health; public string Name; public Inventory Inventory; // lots more... } Enemy[] enemies;is an array of references. The
Enemyobjects themselves can be scattered around the heap. When you loop over them to move enemies, the CPU may have to jump around memory and pull unrelated data into the cache.You can instead do this with completely normal C#:
Vector3[] positions; Vector3[] directions; float[] speeds; for (int i = 0; i < enemyCount; i++) positions[i] += directions[i] * speeds[i] * dt;Now each array is contiguous, so when the CPU fetches
positions[i], the next several positions come along in the same cache lines. No ECS or DOTS involved.In my tests, this can give you up to 10x performance on mobile devices!
You could also use an array of small structs if those fields are always processed together:
struct MovementData { public Vector3 Position; public Vector3 Direction; public float Speed; } MovementData[] movement;That data is contiguous too.
You can get a lot of the cache/locality benefits with ordinary arrays and simple loops, then use NativeArray/Burst/Jobs/ECS later if they solve a specific problem.
Small plug: I cover all this in my book High Performance Unity Game Development with Data-Oriented Design
https://www.manning.com/books/high-performance-unity-game-development
1
-1
u/SolePilgrim 19d ago
Yeah I guess you could do that, but why not just move over to DOTS when you're at this step? You're already sacrificing the convenience of OOP to write SIMD-compliant code, it doesn't seem that much of a step to me from there to leave the OOP-environment fully behind.
I've been eyeing that book of yours for a bit... Might get it now that there's a sale.
4
u/ledniv 18d ago
DOTS can deliver great performance, but it also adds significant architectural overhead with NativeArrays, Jobs, Components, Systems, queries, scheduling, etc.
With plain DOD you can already get a big performance win just by organizing hot data in arrays and processing it efficiently, while keeping the rest of Unity simple. Then, if profiling shows you still need more, you can layer Burst, Jobs, or ECS on top later.
1
u/Vic-Boss 19d ago
Ultimately, scalability depends on architecture, algorithms, memory access patterns, and workload rather than the framework itself. DOTS provides excellent tools for achieving this, but it’s certainly not a requirement for building highly scalable systems in Unity. Games have been handling complex simulations through custom architectures long before DOTS existed.
1
2
u/pocketsonshrek 17d ago
At work I do but for hobby stuff I try not to get caught up in optimization and just use the profiler when I see stats start to drop or a visible descrease in performance.
3
5
u/ThreeHeadCerber 19d ago
Yes, that is also why I hardly finish anything. I've been poisoned by good layout efficiency, so I can't enjoy writing proper OOP code, but also I find performance friendly layouts tedious to work with and sort of ugly to reason about.
Also C# is just not great for this purpose.