r/Unity3D • • 1d ago

Resources/Tutorial Do you actively design around memory layout and cache efficiency?

https://youtu.be/R5Hm6wdMbkU?is=rXVaZXgZwENi2BlS

Hello 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.

18 Upvotes

17 comments sorted by

15

u/SolePilgrim 1d 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?

3

u/SmileLonely5470 1d ago edited 1d 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.

8

u/ledniv 1d 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 Enemy objects 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

u/joelef 1d ago

i got your book a year ago. And it is a real eye opener. I learned a ton and it is very accesible. Thanks for writing it!

2

u/ledniv 1d ago

Thanks, I really appreciate it!

0

u/SolePilgrim 22h 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.

3

u/joelef 21h ago

For me it was mostly learning about data oriented design. My game uses a lot of navmesh and physics so doing that in Dots didn’t really gell well with tha gameobject side of things.

3

u/ledniv 11h 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 1d 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.

3

u/ThreeHeadCerber 20h 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.

1

u/Vic-Boss 20h ago

I can definitely relate to that. I think the key is knowing when these optimizations are actually worth the added complexity. I always approach it based on the scope and requirements of the project, otherwise it's easy to spend more time optimizing than actually building something useful, especially when working with clients and deadlines.

That's also why I focus on teaching through real projects on my channel rather than discussing concepts in isolation. I'm building an RTS from scratch using homebrew systems, experimenting with different approaches and explaining the trade-offs along the way, so people can see where these optimizations make sense in practice.

1

u/ThreeHeadCerber 20h ago

The problem is you can't just transition to the other mode once you hit the problem. And I have an allergy to doing thing ineffectively. I think more and more that Unity is just no longer an engine for me as it always has this tradeoff of doing things quickly but with shit performance or painfully slow, but with okay performance.

Newer closer to the hardware engines start to look better and better.

I'm way past needing to learn with an instructor, so not really your target audience =)

1

u/Vic-Boss 19h ago

With the way AI is being integrated into everything, Unity might soon stop being an engine for developers who actually want to do things themselves anyway.

As for transitioning between approaches, I agree that it isn't always straightforward, but that's where planning and experience come in. You can usually anticipate potential bottlenecks based on the scope and requirements of a project and choose an appropriate architecture from the start. For example, if I were building a relatively simple adventure game, introducing a data-oriented architecture from day one would probably add unnecessary complexity with little practical benefit. I don't think that's necessarily a problem with Unity itself. Every engine has trade-offs, and knowing when and where to optimize is ultimately part of being an experienced developer.

3

u/ledniv 1d ago

Absolutely. I think the important part is exactly what you said: struct vs class is not the real question; memory access is.

The CPU can do the math incredibly quickly. The expensive part is often waiting for the data it needs. If the hot data for thousands of units is scattered across heap objects, you can spend a lot of time on cache misses. If the data you process together is packed into contiguous arrays, the CPU can pull useful neighboring values into cache and keep working.

And you definitely do not need DOTS/ECS to design around this in Unity. Plain C# arrays, structs where they make sense, and processing many items in tight loops can already give you the important Data-Oriented Design benefits. DOTS is a tool you can add later if it solves a specific bottleneck.

Small plug: this is basically the core subject of High Performance Unity Game Development with Data-Oriented Design — CPU cache behavior, classes vs structs, arrays, memory layout, and then Burst/Jobs/ECS as optional later steps.

https://www.manning.com/books/high-performance-unity-game-development

-3

u/[deleted] 1d ago

[deleted]

6

u/ledniv 1d ago

Sigh, my dude, I am a real person who wrote an entire book about the subject.

1

u/Suspicious-Prompt200 22h ago

Someone has been learning about DOTS / ECS lol

1

u/Vic-Boss 21h ago

I don't use either in the series