Live data from Hacker News

An Introduction to Data Oriented Design with Rust

jamesmcm.github.io

51–60 of 161 posts

Re: An Introduction to Data Oriented Design with Rust

#51
post #31

Earlier quoted context omitted.

> DOD generally implies a high level of coupling on the system. That's it's biggest weakness. I'm not sure I understand what you mean by DOD generally implies a high level of coupling? Could you perhaps elaborate a bit?

Lets say you have "struct Point {int x; int y;};", and all things are fine and dandy. You write your OOP as such (or your DOD version), and things are going great. Suddenly, you want "struct Point3D {int x; int y; int z;}". How would you change the code? In OOP, maybe you'd compose a Point3D as such: struct Point3D { Point xy; int z; }; You can now "capture" all of the Point xy; functionality, and extended it to a 3r…

That's one of the most contrived examples I've ever seen.

Re: An Introduction to Data Oriented Design with Rust

#52
post #31

Earlier quoted context omitted.

> DOD generally implies a high level of coupling on the system. That's it's biggest weakness. I'm not sure I understand what you mean by DOD generally implies a high level of coupling? Could you perhaps elaborate a bit?

Lets say you have "struct Point {int x; int y;};", and all things are fine and dandy. You write your OOP as such (or your DOD version), and things are going great. Suddenly, you want "struct Point3D {int x; int y; int z;}". How would you change the code? In OOP, maybe you'd compose a Point3D as such: struct Point3D { Point xy; int z; }; You can now "capture" all of the Point xy; functionality, and extended it to a 3r…

You wouldn't have to mingle them if they're different types, in fact that would probably be one of the worst design decisions you could make.

In the DOD version you'd create a new record containing 3 arrays instead of 2 (x,y,z) and write or extend your functions to support this new record type.

In the OOP version (NB: blech, it's not OOP, it's just records) you'd also need new functions to support the new type.

There are definitely benefits and drawbacks to both approaches, but what you've written is not a drawback for structure-of-arrays or a benefit for array-of-structures.

Re: An Introduction to Data Oriented Design with Rust

#53
post #42

Earlier quoted context omitted.

A lot of optimizations can't be easily added later without redesign. But it doesn't mean they are always necessary. Question is, how deep to go first.

Ok, but this one is literally an upfront architecture decision.

Right, but it's optimization focused one, so that was my question.

Re: An Introduction to Data Oriented Design with Rust

#54
post #45

Earlier quoted context omitted.

The interesting thing to me from these patterns is the possibility of using proc_macros to write code in the "array of structs" style while the code gets desugared to "struct of arrays". I don't know how beneficial that would be, but having it as an option would make this pattern more approachable to people that would otherwise not use it, due to verbosity, mental model mismatch or any other reason people might have…

It seems likely to me that Rust will use ECS queries as the idiomatic way to write struct-of-arrays code. In archetype-based ECS libraries (which the ecosystem seems to be converging on or near) you write a query asking for a particular subset of "component" types, where each type is stored in its own array(s), and then for each iteration of the query you get back one reference to each component type. As a result, al…

Tiny nitpick: that turbofish syntax wouldn't be possible due to lack of support for variadic type parameters, but otherwise this seems reasonable.

Re: An Introduction to Data Oriented Design with Rust

#55
post #45

Earlier quoted context omitted.

It seems likely to me that Rust will use ECS queries as the idiomatic way to write struct-of-arrays code. In archetype-based ECS libraries (which the ecosystem seems to be converging on or near) you write a query asking for a particular subset of "component" types, where each type is stored in its own array(s), and then for each iteration of the query you get back one reference to each component type. As a result, al…

Tiny nitpick: that turbofish syntax wouldn't be possible due to lack of support for variadic type parameters, but otherwise this seems reasonable.

Whoops- the actual libraries I'm thinking of wrap those args in a tuple. I've updated the example.

Re: An Introduction to Data Oriented Design with Rust

#56
post #53

Earlier quoted context omitted.

Ok, but this one is literally an upfront architecture decision.

Right, but it's optimization focused one, so that was my question.

No, it's an architecture. The speedup is a nice corollary.

Re: An Introduction to Data Oriented Design with Rust

#57

Earlier quoted context omitted.

Lets say you have "struct Point {int x; int y;};", and all things are fine and dandy. You write your OOP as such (or your DOD version), and things are going great. Suddenly, you want "struct Point3D {int x; int y; int z;}". How would you change the code? In OOP, maybe you'd compose a Point3D as such: struct Point3D { Point xy; int z; }; You can now "capture" all of the Point xy; functionality, and extended it to a 3r…

You wouldn't have to mingle them if they're different types, in fact that would probably be one of the worst design decisions you could make. In the DOD version you'd create a new record containing 3 arrays instead of 2 (x,y,z) and write or extend your functions to support this new record type. In the OOP version (NB: blech, it's not OOP, it's just records) you'd also need new functions to support the new type. There…

> In the DOD version you'd create a new record containing 3 arrays instead of 2 (x,y,z) and write or extend your functions to support this new record type.

But now you're violating DRY: don't repeat yourself. Bugfixes found in Point3D will have to be manually "ported" to Point functions (and vice versa).

> In the OOP version (NB: blech, it's not OOP, it's just records) you'd also need new functions to support the new type.

Those new functions can largely be written as foo3D(Point3D a, Point3D b) {return foo(a.xy, b.xy); }.

DRY is kept, your bugs fixed in foo will automatically apply to foo3D. If you use inheritance (which... probably would be a mistake... but its possible and an "OOP" solution), your Foo functions would automatically work on Point3d.

EDIT: Hmm... maybe Point and Point3D keep the Liskov Substitution Principle and therefore work for inheritance. Maybe inheritance is a valid solution in this case?

Re: An Introduction to Data Oriented Design with Rust

#59

Earlier quoted context omitted.

ECS style DOD arguably makes putting things together easier though, because instead of having to update your actual object when you want to make a change, you only have to add the right component that contains the functionality you need to the given entity ID.

And don't get me started once you have some hierarchies. Sometimes a object changes and you need to notify a parent of this change. OOP really sucks at this.

It seems like in these cases often at least in frame-based systems like games it makes sense to sort your data in a parent->child partial order, then visit in that (or reverse depending on which algo) order propagating changes, then do another pass later and so on. It's just nice that you can think about these things once you're in DoD-land.

Re: An Introduction to Data Oriented Design with Rust

#60

Earlier quoted context omitted.

You wouldn't have to mingle them if they're different types, in fact that would probably be one of the worst design decisions you could make. In the DOD version you'd create a new record containing 3 arrays instead of 2 (x,y,z) and write or extend your functions to support this new record type. In the OOP version (NB: blech, it's not OOP, it's just records) you'd also need new functions to support the new type. There…

> In the DOD version you'd create a new record containing 3 arrays instead of 2 (x,y,z) and write or extend your functions to support this new record type. But now you're violating DRY: don't repeat yourself. Bugfixes found in Point3D will have to be manually "ported" to Point functions (and vice versa). > In the OOP version (NB: blech, it's not OOP, it's just records) you'd also need new functions to support the new…

So your OOP version creates many layers which impedes understanding and extension as well.

DRY is also not a law which must always be followed. You often don't even know when you're repeating yourself until you do, and the first time you see a repetition is not when you should eliminate it (in most cases). You often see people reference the rule of three here.

The first repetition should make you pay attention. The second lets you know that it's probably (not necessarily) time to refactor. Premature DRY is as bad as premature optimization.

Post reply on HN