I once have a misunderstanding that “jobs are the fastest, jobs are justice” therefore even with simple operation like destroying entities from very easy component group criteria, I made a job for it.
Bad : Schedule a job just to queue ECB
It is true that worker threads are great but the mistake is that ECB. Actually EntityCommandBuffer is SLOW. It is just EntityManager doing DestroyEntity one by one later at the barrier in the main thread, it is not like they are being destroyed in a job.
The purpose of ECB is to defer EM commands, not to speed up EM commands. And therefore this job is a complete waste of time.
Good : Just use EntityManager right there
Even iterate destroying with EM directly, naively, will be faster because you don’t schedule a job!
Be careful of invalidating the thing you are iterating on. Use PostUpdateCommand and iterate destroy should be faster on low number of entities, the jobbed ECB version may win with large enough amount and more chunks to exploit with things like IJobChunk? (It became a race of queuing commands)
Better : Use batched overload
But anyways with component group or NativeArray<Entity> it could destroy in batch in the main thread. Just do like the commented code. Too bad other operations does not yet comes with batched version (preview 21)
Right now the inside is not really batched in the sense of instantly disappear together, they will be iterate destroy one by one no matter what overload you use.
- The single entity overload just make a 1 element pointer array, then it joins with NativeArray overload.
- The ComponentGroup overload made a NativeArray for you then joins with NativeArray overload.
- NativeArray overload’s innermost is a while loop over all elements. So not much different from the ECB case really.
Notice the code comment, soon it might be able to just make a whole chunk disappear if you use the ComponentGroup overload, so I think try to onboard with that.
Maybe even better : Use ExclusiveEntityTransaction
But if you wanna destroy (or other EM operations) in the job for real, then use ExclusiveEntityTransaction
EET is like an inverse of what normally occurs. Normally to do things to EM we have to “come back” to the main thread for a moment (at barriers, at ComponentSystem, etc.) With EET, we can “lock the EM” for one thread to work on and prevents the main thread from using it. But the main thread can go on and do other things, including scheduling more jobs which do not touch EM.
No one in the world would be able to use that EM in the main thread while the job is running, but other world’s EM of course works fine. One worker thread would be unavailable though, because EET is taking place in the other world. (Go! Now! If you want it!)
So the use of EET is heavily geared towards having multiple worlds. It renders 1 world near unusable (EM became busy in-job) but your other worlds may still work on with their own EM and the remaining worker threads. Now you see something that only multiple worlds can achieve!
But to make multiple worlds useful to each other it would requires more careful planning how to communicate. (Which is probably by EntityManager.MoveEntitiesFrom ?)
“Throwing away” by having the other world do EntityManager.MoveEntitiesFrom your main world
This method has ComponentGroup overload which from roughly looking, runs a job that cuts off queried chunk pointers and hand it to the 2nd EntityManager . No copy or anything, but with some entity remapping (Index and Version will adapt to the new world). And so it should be the fastest way to “get rid” of chunks you originally intended to destroy. (I think?)
Remember that ComponentGroup must be made from the world owning things you want to move! CG is not interchangable between worlds.
Also this overload asks for entity remapper NativeArray working space from the caller, which you can get from EntityManager.CreateEntityRemapArray . You will get just enough size for the method to work. (Please make it from EntityManager of the world you are moving FROM) As a bonus you get the remap result back, but it is safe to just dispose it immediately or just use using on the EntityManager.CreateEntityRemapArray .
Then once those chunks are in a “garbage world” then you could use EET to destroy them for real in job or just destroy that world should be even faster. (I guess?)
Not just for destroy, if you are looking to mass-add/remove a bunch of components with heavy conditional to like 10000 entities, if you do it in the job of one world with concurrent ECB then inevitably on the playback it will stop everything in your world.
Could be better to move those 10000 entities to 2nd world where you can work full power with EET in a job, your 1st world do something else in the mean time.
Finally when your 1st world run out of things to do unless it has the processed components, move them back. This step should be a bit difficult to determine “when”, as you don’t have JobComponentSystem’s dep chain to manage this dependency for you. Possibly, position a ComponentSystem conditioned with UpdateAfter all systems which does not require the thing processing in the 2nd world. This system then MoveEntitiesFrom , and all other systems which requires the processed entities should be conditioned with UpdateAfter this world sync system. (Use UpdateInGroup ?)
Hopefully : We could have “chunk operation”
If we can get a new set of commands for EntityManager which takes ArchetypeChunk as a whole I can imagine destroy/add/remove/whatever will be even faster. Often I do the same thing to everyone in the same chunk, like removing a tag component.