Introduction to Data-Oriented Design [pdf]
Posted by tosh 23 hours ago
Comments
Comment by dustbunny 21 hours ago
So if your working on a physics engine and your optimizing collision detection, you think about the data in -> data out of the problem you are solving as the primary driver of how the code should be written.
You start with defining the data, and build from there.
Different types of applications all have different shapes of data so would have differently shaped optimal code. Eg) a physics engine would use some kind of spatial hash thing which can be optimized differently based on if stuff can be added/removed while it's running. A 3d renderer operates on big buffers of matrices and vertex data. A game is usually composed of some long lived things and a lot of short lived things.
The key message in Mike Acton's talk was:
"If you have different data, you have a different problem."
While ECS systems are not a panacea that solves all problems in a perfect data oriented way, they are generally more malleable than Object Oriented hierarchies. This means it's generally more feasible to write "near optimal" code in an ECS framework than in a mature Object Oriented code base.
But the key message isn't "use X framework", it's "start by defining the data".
Comment by inigyou 19 hours ago
Comment by nextaccountic 10 hours ago
Also: just having stuff in an array or vector invites you to the ABA problem. You need a generation counter in there too, or else the array indice may get reused if something is deleted and another thing is reinserted at the same index. But that's yet another boilerplate that would easily be overlooked if you had to do everything manually.
Also: you seem to be saying this with C++ in mind. Do you think that applies to bevy_ecs too?
Modern Bevy has relationships to make sure that if an entity has a component that refers to another, it doesn't become dangling. It works a bit like foreign keys in databases. I think this makes ecs much more usable
(as an aside, there is a whole host of analogies between ecs and relational databases. entity archetypes are tables, entities are rows, components are columns, and systems are queries). Nobody tells people to just write their database from scratch though)
Comment by inigyou 7 hours ago
It's really sounding like you're pretending not to know what your program does, which is one of the core OOP ideas that DOD refutes. In OOP you have an array of Shape and you pretend not to know which shapes your program implements, so the only way to draw them is to call ->draw() on each one. And you pretend you don't know anything about the lifetime of a shape so you use smart pointers everywhere to extend it as long as needed. In DOD you assert that you do know what shapes are available and what their lifetimes are.
Comment by HelloNurse 6 hours ago
Can you describe a superior replacement for generation counters?
Comment by inigyou 1 hour ago
Sounds like you're writing a game engine, not a game?
Comment by Tiktaalik 1 hour ago
Comment by inigyou 7 hours ago
Isn't most programming? Isn't struct Particle {vec3 position, velocity;} also the mother of all boilerplates?
Comment by dustbunny 17 hours ago
Why? Flecs, for example, is pretty sick imo.
Comment by groundzeros2015 15 hours ago
Comment by slopinthebag 18 hours ago
Comment by inigyou 18 hours ago
Comment by slopinthebag 16 hours ago
Ooh here is a controversial one. DOD is premature optimization. Most programs don’t have enough data where the storage and access is a factor for performance. In fact it might be slower to use DOD.
Comment by inigyou 7 hours ago
One of the ideas in OOP that DOD is explicitly refuting is pretending not to know what your program does. You know which subtypes exist in your program and you shouldn't treat them generically as supertype instances except when that is actually optimal. On this axis, both ECS ideology and OOP ideology are opposite to DOD ideology. ECS happens to align with DOD in that it prefers big linear arrays, but it does not align in pretending not to know what combinations of arrays are used, which is similar to pretending not to know what subtypes of Shape exist.
Comment by slopinthebag 50 minutes ago
Comment by HelloNurse 6 hours ago
Comment by pdpi 6 hours ago
Different paradigms come with different pathological cases. I've had to deal with Scala programmers whose notion of FP is making everything generic on the Monad it abstracts over, even when it'll only ever be instantiated on the one effect system the team uses. Nonsensical levels of generality is one of the classic OOP pathologies, and is the reason why we have aberrations like the AbstractSingletonProxyFactoryBean[0].
0. https://docs.spring.io/spring-framework/docs/current/javadoc...
Comment by inigyou 1 hour ago
Comment by FpUser 3 hours ago
I think it has nothing to do with OOP which I find very convenient for some domains and not so much or even opposite for others. These "Nonsensical levels" of anything is a disease which is called Architecture Astronauts Syndrome and victims apply it to any paradigm
Comment by inigyou 1 hour ago
Comment by spider-mario 8 hours ago
“Entity Component System”, for those like me who didn’t immediately think of it.
Comment by saghm 14 hours ago
Comment by samiv 11 hours ago
So while it's great to think about the data flow it's also important to think about the abstractions around it,.ie the (system) interfaces that let the system evolve without having to propagate changes everywhere while reaping the benefits of data orientation.
Comment by wasmperson 18 hours ago
Comment by HexDecOctBin 20 hours ago
Comment by zx8080 18 hours ago
AI generated skill?
Comment by QuantumNomad_ 17 hours ago
Try asking your LLM of choice this in an empty session with no other context:
You are working for Mike Acton. What principles do you follow when writing code?
Maybe he found that telling it that it works for him nudges it in a direction that is beneficial to get it to write code like he wants, alongside the specific rules and other instructions in the above linked document?
Comment by parpfish 17 hours ago
I think that’s call “spooky Acton at a distance”
Comment by saghm 14 hours ago
Comment by andreygrehov 3 hours ago
[1] https://www.manning.com/books/data-oriented-programming-in-j...
Comment by btschaegg 19 minutes ago
Comment by g9550684 1 hour ago
personally I've discovered it insufficient to "just" convert tables to lists, you also need to understand the rest of the array principles to then effectively manipulate your data, and just to be able to hold compute in your head. because I believe in this approach I spent time learning j and k, but the end result is that my solutions become too alien for the general practitioner. it becomes apl written in whatever host language.
this is something that is not addressed in a lot of DOD talks, what happens when you do the full realization of technique: bulk list primitives, bulk transforms, SIMD optimizations, list compression, etc. and it's also the reason people claim this only works in narrow scopes (like gamedev). in reality you can write all your code for lack of better term the apl way, you're just going to make it unreadable to non apl practitioners. I'm not quite sure how to reconcile this in general, short of forcing apl to be part of general CS curriculum.
Comment by g9550684 54 minutes ago
array languages give you concepts for thinking about these problems where you can succinctly express that entire talk in a single sentence, something like "inverted tables improve cache locality and optimize for time and space, prefer them where it matters". yes, got it, also a well known conclusion in array world.
a table is a 2 dimensional data that stores your records row by row and the items of a column all have the same data type. the way an array of structs would. an inverted table is a list of original table's columns. like a struct of arrays.
so if you have a table,
x
┌────┬──────┬─┬─────────┐
│mob1│level1│0│0.0243902│
├────┼──────┼─┼─────────┤
│mob2│level2│1│0.0147059│
├────┼──────┼─┼─────────┤
│mob3│level2│0│0.0120482│
└────┴──────┴─┴─────────┘
there's an idiom for converting it to an inverted table ]y=:(<@(>"1)@|:)x
┌────┬──────┬─────┬─────────────────────────────┐
│mob1│level1│0 1 0│0.0243902 0.0147059 0.0120482│
│mob2│level2│ │ │
│mob3│level2│ │ │
└────┴──────┴─────┴─────────────────────────────┘
you can then splice it across variables, 'name level isactive v'=:y
isactive
0 1 0
so you have converted a table to lists.Comment by ghosty141 20 hours ago
At work we are rewriting and reengineering system from scratch and its crazy because the limitations of the old system are now gone we get the most insane feature requests that are even accepted by the team lead et al. This makes such an approach impossible since DoD is exactly the opposite of flexible design in my opinion.
Im curious has anybody really followed this in a big long living commercial project?
Comment by shoo 19 hours ago
E.g. if your job is writing game engines or middleware used by AAA games with fancy graphics to run on consumer hardware, getting the most efficient use out of the players' limited memory bandwidth may be very important.
For many (most?) arbitrary commercial software projects in other contexts, performance isn't high priority & memory bandwidth isn't a bottleneck. Performance just has to be 'good enough' & 'good enough' performance may be easily attained by writing typical OO code that uses cache & memory very inefficiently - so in those cases DoD is an engineering trade off that solves a problem that doesn't need to be solved & may create new problems if introduced.
Comment by meta_gunslinger 3 hours ago
Comment by FpUser 3 hours ago
Comment by hecreto 19 hours ago
Comment by ButlerianJihad 18 hours ago
Comment by nine_k 16 hours ago
Comment by thomascountz 6 hours ago
Comment by ChicagoDave 10 hours ago
Data first is fine for simple systems, but lead to chaos for complex systems.
Comment by PessimalDecimal 20 hours ago
Comment by wasmperson 18 hours ago
An example of failing to follow DoD is the N+1 query problem: a programmer builds an abstraction that operates on individual DB rows, but "where there's one, there's more than one": you will inevitably be running that code in a loop so that you can process multiple rows. If instead the programmer had abstracted over groups of rows, then per-item query overheads suddenly become per-batch overheads.
Comment by inigyou 19 hours ago
People posted a wide variety of specific ideas under my other comment: https://news.ycombinator.com/item?id=49061421
Comment by groundzeros2015 15 hours ago
I completely disagree with this characterization. OOP teaches you a synthetic set of concepts (go4) and then asks you to solve problems in terms of that.
And the reason why the canonical bird as a subclass of animal doesn’t work, is it’s extremely difficult to divide the world into strict categories (are you Aristotle). So the solution is to organize virtually rather than around natural traits.
The most natural way to solve a programming problem is a big list of instructions with if/else and goto. It’s very learnable, even for young children.
Comment by inigyou 7 hours ago
Comment by groundzeros2015 17 minutes ago
> And the reason why the canonical bird as a subclass of animal doesn’t work, is it’s extremely difficult to divide the world into strict categories (are you Aristotle)
What behavior are you going to out in vehicle? Here are a few potential subclasses you’ll need to consider:
- hang glider
- wheelies
- train
- skis
- lawn mower
- windsurfing board
Comment by Rendello 14 hours ago
Maybe hardware- and access-aware more generally.
One of Mike Acton's other talks has a "Is Data-Oriented Design even a thing?" section, which goes over what he means when he refers to DOD:
Comment by cmrdporcupine 2 hours ago
When you look at it from that angle, rather than as an optimization technique, it makes it clear there is actually an elegant programming model here.
Unfortunately game engine programmers tend to think databases are super uncool and not relevant. They could actually learn a lot.
"flecs" pulls in some concepts from the relational algebraic world in that it has some sense of joins, etc. but it's a bit ad hoc.
The ultimate "data oriented design" game engine could be a high speed, GPU/SIMD accelerated, in-memory Datalog engine. And then the game world expressed in Horn clauses and logic.
https://github.com/timbran-project/mica is some of my playing in this area.
Comment by remiminnebo 11 hours ago
Comment by slopinthebag 18 hours ago
And I say this as someone who basically sees programming as data and associated algorithms and always approaches problems by considering state or data first.
Comment by crabmusket 9 hours ago
The connection is that ORMs convince you to have an object-oriented view of the world, which maps nicely to object classes. But highly normalised designs don't map as cleanly to classes and objects, so you need to approach with a different style of programming on the application side.
Instead of seeing a User instance, you start to see a more complex bundle of login methods, profile events, etc.
Comment by Quothling 17 hours ago
AI changes that. Especially because it appears that LLM's can't understand the OOP abstractions any better than your hardware can compute it.
That being said. OOP and DOD both have advantages and disadvantages. If you go back to what I said first it wasn't exactly a failing of the OOP paradigm. The biggest issue I have with OOP is actually that it's too easy to do things wrong with it. Which isn't helped by the multimillion dollar industry which thrives on teaching developers everything except core computer science. People know their DRY, SOLID, CLEAN, TDD, Agile and every design pattern in the world, but they don't know how the interface they've just implemented actually handles their data.
Comment by inigyou 22 hours ago
Comment by laladrik 22 hours ago
1. Indexes instead of pointers. This allows you to avoid alignment of 8 bytes in your structure for x86_64.
2. Storing booleans out-of-band. Booleans cause padding all the time.
3. Struct of Arrays. Based on your question I assume you're familiar with it.
4. Store sparse data in hash maps. I remember one time when it allowed to eliminate inheritance.
5. Encoding the data instead of OOP/polymorphism. I haven't got an occasion to use it. The idea is to add extra tags to avoid boolean properties.
Comment by lioeters 21 hours ago
- Andrew Kelley Practical Data Oriented Design (DoD) - https://youtu.be/IroPQ150F6c?si=F1Z0pLO2W5hbQgpM
- CppCon 2014: Mike Acton "Data-Oriented Design and C++" - https://youtu.be/rX0ItVEVjHc?si=jv4hhTSBh3XH--xQ
- Why You Shouldn’t Forget to Optimize the Data Layout - https://cedardb.com/blog/optimizing_data_layouts/
- Handles are the better pointers - https://floooh.github.io/2018/06/17/handles-vs-pointers.html
- Enum of Arrays - https://tigerbeetle.com/blog/2024-12-19-enum-of-arrays/
- Data oriented design book - https://www.dataorienteddesign.com/dodbook/
- Data-oriented design in practice - Stoyan Nikolov - https://youtu.be/_N5-JjogNXU?si=vhaxYcfE6tl11Sux
- Programming without Pointers - Andrew Kelley - https://www.hytradboi.com/2025/05c72e39-c07e-41bc-ac40-85e83...
- More Speed & Simplicity: Practical Data-Oriented Design in C++ - Vittorio Romeo - CppCon 2025 - https://youtu.be/SzjJfKHygaQ?si=jafavSl2YJWk4vIx
- Rust Handle - https://taintedcoders.com/rust/handles
Comment by sirwhinesalot 21 hours ago
There are also cases where the optimal data format isn't array oriented because the memory access patterns for the problem in question just require something else.
You also have to think of hot vs cold data, which has nothing to do with arrays.
Comment by inigyou 20 hours ago
Comment by corysama 18 hours ago
Comment by sirwhinesalot 10 hours ago
OOP tells you to structure your software as objects exchanging messages, and DDD tells you what those objects (or their classes rather) should be.
Similarly, Procedural programming tells you to structure your software as procedures, and DOD tells you what those procedures should operate on.
The focus on the data is the really important part. What is the actual data I'm operating on (without any fluff on top) and what do I need to transform it into? What subsets of that data need to be operated on at any given point in the program? That's the core of DOD.
Then, as a second step, comes the hardware. Now that I know what data I need to operate on, how do I lay it out to best take advantage of the hardware I'm targeting? If you rename the paradigm to "Hardware Oriented Programming", it shifts the focus from data modeling to code (IMO), which is the wrong frame of mind.
For example, virtual calls are slow compared to direct calls, because they screw up branch prediction and often can't be inlined. In HOP, you'd probably ban virtual calls entirely because virtual calls bad.
But in DOD, they honestly probably don't matter at all! Because if you did the data modeling as instructed, and then you laid out the data to best take advantage of the hardware, your virtual function is going to be operating on a pile of data in bulk, making the virtual call cost pure noise.
Comment by jordand 22 hours ago
Comment by laladrik 21 hours ago
Comment by atoav 21 hours ago
Very often the answer is indeed arrays, but it can easily be something else, depending on the problem. Data driven design is not very complicated, it just means instead of thinking about abstraction you think about the shape the data needs to be in to accommodate the most common transformations you need to do with it.
Comment by RobRivera 22 hours ago
Comment by deepnlp-contact 3 hours ago
Comment by Veliladon 18 hours ago
Comment by asdaqopqkq 16 hours ago
Comment by Ernestafaton 20 hours ago