9 ms·
There is some middle ground between data-oriented design and OOP: just organize your objects in such a way that: a) objects of the same type occupy continuous
by axilmar 5y ago
There is some middle ground between data-oriented design and OOP: just organize your objects in such a way that:
a) objects of the same type occupy continuous blocks in memory,
b) messages are passed to objects of the same type, then to objects of another type etc.
In this way, you don't lose the advantages of encapsulation, inheritance, polymorphism etc but you also don't sacrifice cache coherence much.
OOP does not enforce a 'random' memory access order, you can very will organize your objects in such a way that speed is not sacrificed much.
- bruce343434 5y ago> objects of the same type occupy continuous blocks in memory, Depending on the language, a single object may have a lot of overhead that adds up in an array. What you often see is one ArrayObject with arrays of properties, kind of like a transposition. A problem there is that in memory the arrays are of course laid out one after the other, which actually destroys cache locality if you need to access more than 1 property inside a loop (it will need to load back and forth to the different property arrays), so it's a somewhat dumb approach. But, at least it saves the overhead, so maybe not too bad. And in a high level interpreted language like php you likely weren't gonna get cache locality anyway. The point is to group all properties you are going to be accessing in a hot loop together in a small-ish array. C has structs for this, 0 overhead "entities" (although they may be padded to multiples of 4 bytes, so keep that in mind). You have compiler specific keywords to forego padding ("struct packing"), or maybe you're lucky and the data just fits exactly right. Either way, in such cases an array of structs is imo the most sane way to go. In fact, C++ offers classes and structs. In my opinion, struct should be used for entities like "weapon" or "car". CLASSES (or objects) should be unix-philosophy adhering miniprograms that do one task and do it well (oh hey, it's the single responsibility principle!). They way most programmers write OOP is a pretty convoluted way to model actual entities anyway. car.drive()? Oh? The car drives it self? No. agent.drive(car) should be the actual method. Agent, mind you, can be a driving AI, or a human driver, or whatever. Maybe the agent is a part of the car? In that case, use composition, not inheritance. (oh hey, entity component system!)
- skocznymroczny 5y agoOOP doesn't force you do do car.drive(). You can have an abstract agent class/interface with virtual "drive(Car c)" method. The method would be overriden by AIAgent, HumanAgent etc. The car itself would have more basic behavior, such as "accelerate()", "turnLeft()", "turnRight()"
- temporama1 5y agoKill me now
- bruce343434 5y agoI think you are over engineering it. The way I imagine it: Car {float throttle, float brake, float wheel} inherits PhysicalObject {velocity, mass, position} Agent.accelerate(Car c){ c.throttle++; } Agent.drive(Car c){ ... accelerate(c); ... } Human inherits Agent
- orwin 5y agoInterface is the best way to do a clean job in this case.
- jhgb 5y ago> You can have an abstract agent class/interface with virtual "drive(Car c)" method. The method would be overriden by AIAgent, HumanAgent etc. Is that a convoluted way of saying "use multiple dispatch", or am I reading it wrong?
- jcelerier 5y ago> In fact, C++ offers classes and structs. In my opinion, struct should be used for entities like "weapon" or "car". CLASSES (or objects) should be unix-philosophy adhering miniprograms that do one task and do it well (oh hey, it's the single responsibility principle!). Please no. The only thing that matters is the language rules ; any non-computer-encodable arbitrary rule like this on top of the language rules just causes an additional lava layer. There is one difference between class and struct and it's default visibility. Use one or the other according to which causes less tokens to appear in your code
- corty 5y agoOnly in C++. Most other OOP languages do not allow controlling allocation that way. Also, OOP only allows array-of-structs continuous data. Struct-of-arrays and hybrid forms are usually awkward or impossible. And with everything except maybe C++ and Rust, those "structs" in OOP-land do have quite an overhead compared to C structs.
- mytailorisrich 5y agoOOP does not say anything about memory allocation. OO principles are one thing, what specific languages do is quite another.
- corty 5y agoThere are no real OO principles. Ask ten people and you will get ten different answers. OO is defined by the languages and tools claiming to implement it, and the set of principles derived from those is inconsistent and contradictory.
- mytailorisrich 5y agoIt's obviously not so. There are OO principles, which are indeed well-known, and each OO language has its own take on how to implement them. It's not even needed to use an OO language to follow OO design principles. My day job is pure C and we follow OO principles as much as practical.
- jerf 5y agoYou conspicuously don't actually name any OO principles. If you did I'm sure we could find "OO" languages that don't conform to them. My personal definition of OO has been backed down to directly connecting some concept of "method" to a data structure, and some form of polymorphism of those methods depending on what data structure you pass in to some function/method. You may note this is incredibly weak, but it does have the virtue of usefully distinguishing between two sets of languages, and that those two sets will have real differences in how you program them. Beyond that it's hard to create a definition of OO that has the second property; you may be able to split the world into "languages that implement OO visibility rules (private, protected, public) and those that don't", but you'll fail the second criterion, in that languages that just leave everything public aren't meaningfully different to program in than ones that implement the visibility rules. I could create several different sets of "OO principles", which wouldn't be mutually exclusive necessarily but certainly would be distinguishable. Especially the distinction between the silly principle that OO objects should somehow reflect real-world entities, which was the major failure in 1980s/1990s OO principles and has, mercifully, all but died in the modern era but most certainly was at one point an "OO principle", and any of the several sets of OO principles I could name that actually function in the real world.
- inopinatus 5y agoQuite so. There’s a false equivalence in this article between data and encapsulated state, but if that were so then the flyweight pattern and its ilk couldn’t exist.
- megameter 5y agoIf we are speaking of C code, it's not quite so bad as it looks to have somewhat fat structs across multiple arrays, since you can fit 64 bytes in a cache line on contemporary desktop CPUs, and that sets your real max-unit-size; the CPU is actively trying to keep the line hot and it does so (in the average case) by speculating that you're going to fetch the next index of the array. Since you have multiple cache lines, you can keep multiple arrays hot at the same time, it's just a matter of keeping it easy to predict fetching behavior by using simple loops that don't jump around...which leads to the pattern parent suggests, of cascading messages or buffers in groups of same type so that you get a few big iterations out of the way, and then a much smaller number of indirected accesses.
- volta83 5y agoIf you loose vectorization, you might be loosing a 4x, 8x, 16, ... 32x perf difference by organizing your data in such a way that memory operations and data manipulation can't be vectorized.
- moldavi 5y agoWhen you say vectorize, are you referring to loop unrolling? Or SIMD or something?
- simiones 5y agoI have never heard vectorization to refer to anything other than SIMD. Loop unrolling is usually only a useful technique to enable SIMD, as far as I know (at least on modern processors, where branch prediction has greatly decreased the cost of jump instructions).
- jhgb 5y ago> as far as I know (at least on modern processors, where branch prediction has greatly decreased the cost of jump instructions). What about ILP? Can't that benefit from an unrolled loop in some cases? For example if there's a fairly long dependency chain but you might still be able to go through two loop bodies at once instead.
- physicsguy 5y agoI find in simulation codes that lack of awareness of (a) is an absolute performance killer. Generally, it's better to use a pattern for an object that's a container for something - so don't have a 'Particle' object but a 'Particles' one that keeps things stores the properties of particles contiguously. In my old magnetics research area you have at least 8 and more frequently 10+ spatially varying parameters in double precision that you'd potentially need to store per particle/cell.
- ajuc 5y agoThis is kinda what Entity Component Systems do - they implement in-memory relational database for game objects, handle dependenceis and allow your game logic code to run efficiently over them while still keeping the pretense of OOP :) Why pretense? Because behaviors (Systems in ECS terms) are completely separated from data (Components) and data for different game objects (Entities) is kept together in regular or sparse arrays. Encapsulation is nowhere to be seen, code is written to specify the components it depends on and run on these arrays. ECS is very fashionable in gamedev lately as it allows for efficient multithreading, explicit depencencies for each subsystem, cache locality and trivial (de)serialization. Used together with handles (tagged indexes instead of direct pointers) it reduces likelihood of dangling pointers and other memory management bugs.
- tffgg 5y agoECS ist Standard for enterprise web apps as well
- sdeframond 5y agoI am curious as to what you are referring to. Are you thinking of redux-like architectures?
- tompazourek 5y agoI have seen some enterprise web apps, but they never used ECS. Can you please share more details about your experience?
- pphysch 5y agoMay be referring to the common 3 layer architectures (see Fowler's PoEAA) which map closely to ECS: Top layer is for "frontend", whatever that means for the product (UI, sound, simulation, etc.), the stuff with side effects. "Systems". Middle layer is purely functional, for business/domain logic AKA utility functions. The most liquid layer, but should not be confused as trivial. Bottom layer is where state (or a way to access & modify it) lives. Data access layer, component layer, etc.
- fennecfoxen 5y agoYou might be looking for "Entity - Component - System" design, common in video games. Entities are still virtual-world objects like you might expect, but none of them would dare keep track of something like their position or temperature or whatever. Instead, they register a component with the appropriate system, which keeps all the data colocated for efficient physics and the like.