I’m learning DOTS and doing AntPheromones sample from GitHub - Unity-Technologies/DOTS-training-samples: Samples designed as exercises to be ported from Unity GameObjects/MonoBehaviours to Unity DOTS.
Unity 2020.1
Entities 0.13
I’m storing pheromone amount in NativeArray, one element per map cell. Storage is managed by one system. Other job systems read / write this array:
PheromoneSteering
PheromoneDrop
PheromoneDecay
Each system exposes JobHandle, which is used in other systems as dependency. This makes job safety system happy.
However it seems counter productive, because each system must know about other systems. It makes it hard to “insert” new system in the middle of the chain or extend the behavior in different way, because everything seems interconnected.
- Is there a way to make these dependencies automatic?
- it seems to me there might be a way, because system order is defined and when they access shared resource, there’s only one way of setting up dependencies (all previously scheduled readers must finish before running writer, only one writer is allowed at a time)
- Is it a good approach to have exposed NativeArray inside system?
- are there other preferred ways like Singleton or Buffer or something?
- PheromoneDrop job system uses Entities.ForEach and multiple entities can write at single index, is it safe?
- I’ve checked the Profiler and job is scheduled on multiple workers
Thank you for answers.
Regarding small systems vs one large:
I’ve read ECS boid demo and they use one system, which runs a bunch of jobs and handles dependencies internally.
It’s an interesting solution, but sometimes I’d like a little more flexible design, which would allow me to add more systems later easily. Also having big monolithic system seems to be going against the philosophy of ECS, especially when individual jobs are doing things which should be “decoupled”. My system of “dependencies” is not better tho.
Regarding concurrent writes to same index:
I could use atomic operations, but that does not seem correct, is there better solution?
What are the options for global structures like pheromone map or spatial enemy lookup?
- Shared native containers between jobs / systems
- Singleton entity with DynamicBuffer (thanks for suggestion)
- Anything else?
3 Likes
If the code for computing the target index into the array to increment is simple enough, it is probably fastest to just run the scatter algorithm single-threaded. Otherwise, you can create a new NativeArray (or use Dynamic Buffers) for each ThreadIndex and then sum the results at the end. That could be a lot of memory allocation though.
Those are the main two I have seen. I typically prefer (1), but that’s partly because I have an automatic dependency solution that avoids coupling systems.
1 Like
I’ve finished the AntPheromones. When running with Time.timeScale 100 simulation takes about 3ms. I’m pretty happy with the result.
Congratulations to @xoofx and team behind Burst! It’s ****ing awesome!
Turning Burst on improved performance 20x!
Here’s link to my fork in case anyone was curious about the implementation:
https://github.com/OndrejPetrzilka/DOTS-training-samples
Conclusion:
- Used DynamicBuffer for pheromone amounts (on singleton entity)
- it’s great because it resolves dependencies automatically
- pheromone amount is not just “lookup” it’s actual data and this way it’s part of the world
- Exposed NativeArrays - agree with what was said above, I’d probably use it only for lookup structures
- lookup would be managed by a single system with write access
- lookup would be updated through changed filter or write group
- other systems would have read only access
- dependency on a single system seems okay to me (no chains like in my previous implementation)
- PheromoneDrop - rewritten as suggested, job on single thread
- there’s 16384 cells and 1000 ants
- currently there’s 1000 writes into NativeArray by one thread which is still very fast
- the other approach might be useful for different ratio of cells and ants
1 Like