Live data from Hacker News

An Introduction to Data Oriented Design with Rust

jamesmcm.github.io

121–130 of 161 posts

Re: An Introduction to Data Oriented Design with Rust

#121

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…

Is there any particular case in which you'd want Point and Point3Ds to be indexed interchangably in the same array? This is just as cumbersome in the array-of-structs case: some structs are larger than others in the same array, you need some signalling mechanism to know which structs are and aren't Point3Ds (lest you invite the wrath of your optimizer), etc. In Rust the AOS case would be an Enum of Point and Point3D,…

> This is just as cumbersome in the array-of-structs case: some structs are larger than others in the same array, you need some signalling mechanism to know which structs are and aren't Point3Ds

Strong disagree. The traditional OOP approach is passing indirect pointers to everything and incurring an inefficient level of indirection.

Ex:

    vector blah;
    blah.push_back(&somePoint2D);
    blah.push_back(&somePoint3D.xy); 

    foo(blah); // "Function foo" works on Point2D and Point3Ds, none the wiser
    // Note that foo may change *blah[10].x += 5
    // and it updates the original Point2D or Point3D. This
    // wouldn't work for a copied array.
I think OOP's inefficiency is well known and often criticized. Critics are correct: OOP methodologies are inefficient (compared to other techniques). But OOP seems to have a win on various software-engineering metrics: Extendability, Open-Closed, DRY, etc. etc.

> In Rust the AOS case would be an Enum of Point and Point3D, and the SOA case would be two Vecs of ints and one Vec of Options.

Rust is a general purpose language. I'm not too good at Rust, but the AOS case would simply be a Box(Point2D).

> More generally, this sounds like a language support problem

ISPC got SOA types by the way. Check em out if you're interested:

https://ispc.github.io/ispc.html#structure-of-array-types

You don't need a very large SOA-type to take advantage of SIMD or auto-vectorization. You can "gather" into an SOA, then "scatter" back to your main representation.

Re: An Introduction to Data Oriented Design with Rust

#122
post #99

Earlier quoted context omitted.

I wouldn't dismiss the JS ECS frameworks without measurement. Polymorphism has costs, and while dynamic languages work hard to remove them, they still work best when they're able to monomorphize the call site, because that enables inlining without combinatoric explosion from chained polymorphic calls. Having a single type in your array means field accesses, method calls etc. have the potential to be monomorphized. Th…

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

Okay, here you go

https://github.com/thi-ng/umbrella/tree/master/packages/ecs

Optimized Typescript ECS with a demo rendering 100,000 live 3D particles

Re: An Introduction to Data Oriented Design with Rust

#123
post #74

Earlier quoted context omitted.

I'm having difficulty understanding how your proposal works. In the beginning, there was one struct-of-arrays, the Point2D x and y arrays. int Point2D_x[]; int Point2D_y[]; We also have a number of functions that use these two arrays. Point2D_foo(Point2D_index), Point2D_bar(Point2D_index). Then, later, we discovered the need of Point3D code. Leading to the creation of one more struct-of-arrays (or 3-more arrays). int…

Here's a library in Rust that works this way: https://docs.rs/hecs/ You don't just have bare arrays sitting there. You have a separate part of your program that is responsible for managing them, playing a role similar to a relational database. Now, to be clear, DOD/ECS/whatever doesn't mean "change all your Point2D methods to take an index instead of a `this` pointer." That gains you nothing on its own. If your `foo`…

Hmm, I'll admit that this is the first time I've heard of the ECS pattern. So I'm not too familiar with your argument. From your description, and from various links discussing the pattern, it seems like an adequate solution to the problem I posed.

I would argue that an ECS is quite far removed from what the original article was talking about however. But perhaps the original article was too superficial in its discussion of DOD.

Re: An Introduction to Data Oriented Design with Rust

#124
post #64

Earlier quoted context omitted.

I think this is because of Rust. In my opinion the Rust game dev community is overly fixated on ECS. On the bright side, Rust has some damn good ECS libraries.

> the Rust game dev community is overly fixated on ECS. Not just Rust. The game dev community everywhere is infatuated with ECS. It's basically cargo culting. There is a large base of amateur or indie game developers who want to feel like they are doing game development the "right" way. One big aspect of that is performance. ECS has a reputation for efficiency (which is true, when used well in a context where your pe…

> ... so you see a lot of game devs slavishly applying it to their code in hopes that the "go as fast as a AAA game" Gods will land on their runway and deliver the goods.

I watched this sentence unfold with bated breath, waiting to yell "and ze sticks the landing!", only to see it end with "goods" instead of "cargo". It's frustratingly close to perfect, though perhaps to end the paragraph with "cargo", you'd have to begin with some synonym for cargo culting.

Re: An Introduction to Data Oriented Design with Rust

#125
post #79
post #49

A lot more in-depth about caching, avoiding branching etc. can be found in this classic talk by Mike Acton who is Engine Director at Insomniac Games https://www.youtube.com/watch?v=rX0ItVEVjHc Very enlightening and entertaining!

This video is linked in the first paragraph of the article.

So?

Re: An Introduction to Data Oriented Design with Rust

#126
post #49

A lot more in-depth about caching, avoiding branching etc. can be found in this classic talk by Mike Acton who is Engine Director at Insomniac Games https://www.youtube.com/watch?v=rX0ItVEVjHc Very enlightening and entertaining!

Mike works at Unity https://blogs.unity3d.com/2017/11/08/were-joining-unity-to-h...

Re: An Introduction to Data Oriented Design with Rust

#127

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…

> 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. Moreover, everyone would agree that first class functions and functional composition are key characteristics. Contrast that with OOP where you have some OOP enthusiasts arguing that inheritance is a defining feature and others who argue it isn’t. Some argue that the “kingdom of nouns”, banana-gorilla-jungle design is inherently OOP and others argue it’s “bad programming and not true OOP”. Others argue that message passing is required, but many others argue to the contrary. While other programming paradigms have fuzzy edges, OOP has no discernible shape at all.

> And whilst I’m not particularly interested in defending OOP I think describing it as uniquely plagued with bad code is not something anyone should just take axiomatically.

Why are there no equivalent criticisms of FP or DO? We might find FP codebases that are overly abstract or a bit slow, but we don’t tend to find (m)any that are designed such that a Book object holds a reference to an Amazon SDK client or a banana with a reference to the gorilla holding it with a reference to the entire jungle. There aren’t prevalent guidance to write code like that in other communities like there is (or perhaps “was”) in OOP circles. Similarly, there aren’t enterprise-y abstract factory beans or anything like that in the FP world.

Mind you, I’m not dumping on OOP—indeed I couldn’t if I wanted to because it has no agreed upon definition, per its proponents.

Re: An Introduction to Data Oriented Design with Rust

#128

Earlier quoted context omitted.

I think the data-oriented version is very appropriate for a game, where performance is king and there's not that much maintenance afterwards once its done. Perhaps I'm wrong but games seem to be very much a finish and toss it over the wall sort of project. Much less iteration than other contexts.

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.

Re: An Introduction to Data Oriented Design with Rust

#129
Correct if i'm wrong: The general conclusion is Vectors are faster and pattern matching is slower in observable way when generating vectors.

I wonder if it changes when vec items becomes bigger structs and size goes to millions.

Rust dosc says with LinkedLists that you should better use Vec for speed. I think just raw array of bytes is always faster than linked pointers. But LinkedList will probably have less of a memory pressure in cases where you have to maintain large list and remove elements at random while keeping coherency.

from Rust docs: Vector = "A contiguous growable array type, written Vec but pronounced 'vector'."

LinkedList = "A doubly-linked list with owned nodes.

The LinkedList allows pushing and popping elements at either end in constant time.

NOTE: It is almost always better to use Vec or VecDeque because array-based containers are generally faster, more memory efficient, and make better use of CPU cache."

Re: An Introduction to Data Oriented Design with Rust

#130

Earlier quoted context omitted.

Likewise the focus on ECS architecture as being the one true DoD pattern when it’s not necessarily data oriented at all and the unfounded assumption it’s used everywhere in game development when it isn’t . And somehow the idea that DoD is a replacement for OOP when it’s really an orthogonal concern. DoD is about recognising data use patterns in your software and organising your software architecture around them in a…

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

Post reply on HN