Live data from Hacker News

An Introduction to Data Oriented Design with Rust

jamesmcm.github.io

141–150 of 161 posts

Re: An Introduction to Data Oriented Design with Rust

#141

I've read quite a few articles about DOD. I get how DOD is great but there are reasons OOP is still around (surely?) I suppose I'm still hung up on, what are the benefits of OOP that I'm missing? It seems like there are reasons OOP is so ingrained. I can see a couple of reasons that are environmental. For instance, OOP is taught in schools because students are often taught to program before they're taught about compu…

At first I thought that DOD was a new programming paradigm. It is not. DOD is a very smart optimization for dealing with processing arrays. So in short, OOP isn't going away this week.

Re: An Introduction to Data Oriented Design with Rust

#142

Earlier quoted context omitted.

> Whilst it’s impressive you’ve managed to hold an argument with yourself I like to address the boringly predictable responses up front so we can avoid rehashing the same silly arguments over and over. I apologize if that spoiled your fun. > Is immutability a defining feature of FP? Let the battles commence. I think most would agree that immutability is more prevalent in FP even if struct immutability isn’t required.…

alankay on June 20, 2016 Object oriented to me has always been about encapsulation, sending messages, and late-binding. https://news.ycombinator.com/item?id=11940050

I’m well aware of Alan’s definition. Nevertheless relatively few people hold that view.

Re: An Introduction to Data Oriented Design with Rust

#143

Earlier quoted context omitted.

> And somehow the idea that DoD is a replacement for OOP when it’s really an orthogonal concern. I’m sure this is true for some definition of OOP, but I’ve seen too much OOP code that insists on hanging every method off of the most plain-old-data of classes. A Book class has to have a buyFromAmazon() method and consequently each instance has a private reference to an Amazon SDK client, and if you want to buy 10K book…

This is only "bad OOP" if you need to buy 10K books at once and it is actually too slow and there is a significant amount of fat to trim (if you need to call a web service once for every book, incoherent memory access is likely to be negligible). Otherwise it's obvious, cheap to write, easier to get right than more complicated approaches, and good enough. True OOP is OOP that meets goals.

Other approaches aren’t more complicated, and this is worse even if you never need to send books in batch because it tightly couples “book” to the Amazon AWS SDK client. Anything that interacts with books now has to take a dependency on the Amazon SDK. A much simpler, better design would be to just call client.buyBook(book) or client.buyBooks(books).

But more importantly, my point is that you have your definition of whether this is OOP or not but lots of OOP proponents will say that this is not true OOP.

Re: An Introduction to Data Oriented Design with Rust

#145

Earlier quoted context omitted.

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 s…

If you don't care about DRY in this case, that's fine. There's more than one problem with your proposed DOD solution. Your proposed solution violates the open-closed principle. Which means you have to modify "old working code" to get anything done. If your "solutions" to fixing code issues is "rewrite and extend the old code to cover the new case", then it becomes impossible to build libraries. ---------- Lets say Ap…

I think you are dramatically underestimating the differences between operations 2D and 3D space. There is zero chance you would want to re-use the vast majority of your 2D code for 3D points.

Re: An Introduction to Data Oriented Design with Rust

#146
post #134

Earlier quoted context omitted.

If you don't care about DRY in this case, that's fine. There's more than one problem with your proposed DOD solution. Your proposed solution violates the open-closed principle. Which means you have to modify "old working code" to get anything done. If your "solutions" to fixing code issues is "rewrite and extend the old code to cover the new case", then it becomes impossible to build libraries. ---------- Lets say Ap…

> Your solution forces you to rewrite Point2D, which therefore creates a change in ApplicationFoo. I don’t see how this is the case. You don’t change Point2D at all, you simply add a second type Point3D. ApplicationFoo can happily continue to use Point2D from your library while you use Point3D. I think the example of Point2D and Point3D is not very well chosen. Your proposed solution of Point3D = { xy : Point2D, z :…

> I don’t see how this is the case. You don’t change Point2D at all, you simply add a second type Point3D. ApplicationFoo can happily continue to use Point2D from your library while you use Point3D.

Point2D, under DOD, doesn't exist. There's "array_x" and "array_y". See the article under question. Point2D would be the index into the "x" and "y" arrays, or an "id" returned by the ECS system.

The "x" and "y" fields, under DOD, are dispersed into different areas, allegedly for SIMD / auto-vectorization benefits. I'm trying to discuss the effects of a decision like this.

> I think the example of Point2D and Point3D is not very well chosen.

There are multiple users who understand software engineering who disagree with me, but understand what I'm trying to discuss.

There are also multiple users who prefer to be pedantic and focus on this issue. For the most part, I'm able to ignore these unimaginative users pretty easily. So the example is working pretty well as a filter.

If you wish for more people to participate in the discussion without getting distracted, maybe a better example would have been recycling the example from the article:

    pub struct Enemy {
        name: String,
        health: f64,
        location: (f64, f64),
        velocity: (f64, f64),
        acceleration: (f64, f64),
    }

    pub struct FastEnemy {
        name: String,
        health: f64,
        location: (f64, f64),
        velocity: (f64, f64),
        acceleration: (f64, f64),
        jerk: (f64, f64), // 3rd derivative of location
        jounce: (f64, f64), // 4th derivative
    }

    pub struct GalaxianEnemy{
        name: String,
        health: f64,
        location: (f64, f64),
        velocity: (f64, f64), // Constant vertical velocity
        loopiness: (f64, f64), // cos / sin based horizontal movement
    }
Doesn't really matter. There are plenty of data / classes where you need to just "add one more parameter" to a previously created class to make it perfectly work. The above "GalaxianEnemy" and "FastEnemy" classes follow this pattern.

---------

But if we focus on this pedantry, we wouldn't be able to discuss software engineering. Surely you've come across an example in your programming life where code-reuse would be useful with a subset of parameters, but needed to be extended into an additional parameter?

> The main problem with this solution, I think, is that you picked one specific projection from 3D space to 2D space. It might line up with a projection that is relevant to your domain but it probably won’t. So if you use inheritance and call a function requiring a Point2D with one of your Point3D’s you secretly project the point to the xy-plane which seems like something that should be explicit in the code.

Is it so hard to imagine a video game, where my Point2D and Point3D interpretation is in fact correct? The point of the discussion is to point out software engineering principles: recycle code where possible, open-to-extension but closed-to-modification, and other such principles.

Re: An Introduction to Data Oriented Design with Rust

#147

Earlier quoted context omitted.

> And somehow the idea that DoD is a replacement for OOP when it’s really an orthogonal concern. I’m sure this is true for some definition of OOP, but I’ve seen too much OOP code that insists on hanging every method off of the most plain-old-data of classes. A Book class has to have a buyFromAmazon() method and consequently each instance has a private reference to an Amazon SDK client, and if you want to buy 10K book…

Whilst it’s impressive you’ve managed to hold an argument with yourself I think it’s more the case that anything as broad as a programming paradigm will naturally hold multiple approaches. Is immutability a defining feature of FP? Let the battles commence. Likewise critics might focus heavily on inheritance or some other feature of OOP that is easy to critique. And whilst I’m not particularly interested in defending…

I think the problem with OOP is that the paradigm itself doesn't offer much. It's a very generic paradigm with a massive sandbox. It needs to be more opinionated.

Re: An Introduction to Data Oriented Design with Rust

#148

Earlier quoted context omitted.

That is changing over time as post launch support grows and some games shift to games as a service

Are the "games as a service" games adding significant code after launch? I think most of the performance critical stuff is in the engine and core game loops. Content packs and seasons aren't likely to change those core bits, and instead be more assets and scripting intensive.

Destiny 2 just had to announce a complete re-do of how they're doing content now and in the future, because the game is significantly held down by technical debt from previous content.

https://www.bungie.net/en/Explore/Detail/News/49189

Re: An Introduction to Data Oriented Design with Rust

#149

Earlier quoted context omitted.

This is only "bad OOP" if you need to buy 10K books at once and it is actually too slow and there is a significant amount of fat to trim (if you need to call a web service once for every book, incoherent memory access is likely to be negligible). Otherwise it's obvious, cheap to write, easier to get right than more complicated approaches, and good enough. True OOP is OOP that meets goals.

Other approaches aren’t more complicated, and this is worse even if you never need to send books in batch because it tightly couples “book” to the Amazon AWS SDK client. Anything that interacts with books now has to take a dependency on the Amazon SDK. A much simpler, better design would be to just call client.buyBook(book) or client.buyBooks(books). But more importantly, my point is that you have your definition of…

Actually, the Amazon client could be a component that is managed by a sane dependency injection system (e.g. a factory of "books that can be bought on Amazon") and is used by a book to implement the abstract book operation "buy a copy of me". This would be basically equivalent to a "book buyer" object with a "try to buy this book" operation, with small advantages and disadvantages (either the books or the bookstores are privileged as the main entity in the many to many "can be bought at" relationship).

Re: An Introduction to Data Oriented Design with Rust

#150

Earlier quoted context omitted.

> I wouldn't dismiss the JS ECS frameworks without measurement. I think the burden of proof is on the part of JS ECS frameworks to show they do have better performance by virtue of DoD and, if so, why. JS engine programmers have been optimizing object-oriented code for literally forty years, all the way back to when they were making Smalltalk VMs. If somehow a couple of folks hacking on ECS frameworks have managed to…

Modern JS engines won't use a hash table for the object state, they'll use a hidden class, and member accesses will be fixed offset indirect loads guarded by a type check. Initialize your objects carefully in a deterministic order, and I'd expect you can control field order and adjacency. I'd expect the wins from reworking your JS so that your target JS engine lays out data out better would often be larger than the w…

> Modern JS engines won't use a hash table for the object state, they'll use a hidden class, and member accesses will be fixed offset indirect loads guarded by a type check.

The type checks themselves have significant overhead, and it's easier to fall off the shadow class fast path than you might expect.

> Initialize your objects carefully in a deterministic order, and I'd expect you can control field order and adjacency.

True, but that's equally true of non-ECS architectures. I have yet to see much evidence that the ECS JS engines I've looked at are actually taking that into account.

Post reply on HN