fireblade-engine / ecs

A dependency free, lightweight, fast Entity-Component System (ECS) implementation in Swift
MIT License
110 stars 11 forks source link

Contiguous memory storage for your ECS buffers. #46

Open forbiddencactus opened 2 years ago

forbiddencactus commented 2 years ago

Hello! I stumbled upon this project while looking for something completely different and I immediately took interest since I've been writing a Swift hobby game engine and so Fireblade is really interesting to me! I have a suggestion based on my own learnings about Swift to make this ECS system much faster, and I figured I'd drop it here in case it's useful!

Something I learnt from writing my own Swift engine with a data oriented design is that at the moment it's impossible to (easily) contiguously allocate instances of classes in memory... Swift's memory model just doesn't allow for it. In your Component protocol, you enforce an 'AnyObject' inheritance, which means that your components will be reference types and allocated by the swift runtime (which ultimately calls malloc), and the pointers to these objects will be allocated to wherever malloc thinks is appropriate, in a non ecs/cache friendly way. ContiguousArray unfortunately won't fix this problem since it'll just allocate a contiguous array of references to your class instances. ContiguousArray isn't truly 'contiguous' for reference types, as far as I've investigated.

This means that whenever one of your systems is iterating over a group of entity/components, it will likely incur a large amount of cache misses as you access/process the data in every component, and won't be very performant.

I actually stumbled into this problem myself, and even went as far as trying to hack into the Swift runtime in order to solve this issue (and it is somewhat ultimately solveable, with very involved hacking), but ultimately it felt like I was working against the language design, and so I just decided to use value types for my components, so that any contiguous buffers are actually contiguous and cache friendly.

My suggestion is to go with value types for your ECS system, which should solve the problem neatly.

Great job on Fireblade! I really hope to see this develop. :)

ctreffs commented 2 years ago

Hi @forbiddencactus,

thanks for your feedback :-)

Yes you are very right - currently Components are not cache friendly since they are reference types. This is a limitation for now, I'm aware. And you are also right about the value types being the right fit here 👍 RealityKit has a Swift based ECS implementation that uses value types and it's the right approach. Ultimately I'd like to have an Allocator that is managing memory entirely and can be optimized according to the specific needs of the Nexus.

I'd love to refactor FirebladeECS to using value types, but since I'm a hobbyist myself it's a huge undertaking besides the regular job. Maybe you're interested in helping with it? Would love to have you on board. Let me know :-)