GetBuffer slow?

Hello,
I am learning DOTs in my spare time. One bump I’ve hit is that Getbuffer seems to really slow.

I have a simple oxygen system where if you run of air you die. All stat changes are stored in a buffer on the entity and applied at the end of the frame. In my game, “oxygen” only applies to the player so I run the following on the entity stored in the player singleton:

{
    public class OxygenSingletonSystem : ComponentSystem
    {   
        protected override void OnUpdate()
        {
            Entity entity;
            EntityManager entityManager;

            float deltaOxygen;
            float deltaTime;
            float oxygenPerMinuteScale;     


            entityManager = EntityHelper.GetEntityManager();
            entity = EntityHelper.GetPlayerEntity(this);

            if(! entityManager.Exists(entity)) return;
            //Rate of O2  usage
            //Needs to be moved to a table
            oxygenPerMinuteScale = 10.0f;
            deltaTime = Time.DeltaTime;
            //O2 
            deltaOxygen = -1 * oxygenPerMinuteScale * deltaTime;

            //Create a stat queue message
            ChangeStatQueueBufferElement changeStatQueueBufferElement = new ChangeStatQueueBufferElement() 
            { 
                StatType = StatType.Oxygen, 
                Value = deltaOxygen
            };

            //Get the stat queue and add the message to it. 
            DynamicBuffer<ChangeStatQueueBufferElement> statChangeBuffer;
            //This line is really slow -- 5ms w/ 1000 entities in the scene. 
            statChangeBuffer = entityManager.GetBuffer<ChangeStatQueueBufferElement>(entity);
           
            statChangeBuffer.Add(changeStatQueueBufferElement);
        }
    }

The code decrements the oxygen value correctly. Yet, when I add 1000 other entities to the scene the time to run the oxygen system goes from around 0.1ms to over 5ms (from the entity inspector). Specifically, it’s the GetBuffer line near the end that’s taking the time. I am confused about why GetBuffer taking so long and seems to scale with the number of other entities in my scene. Is there something wrong with how I’ve implemented this?

ComponentSystem is really slow in general for 1000 units. You can convert it to JobComponentSystem and schedule a burst compiled job with command buffer to add to your buffer it will work like 100x faster

1 Like

Sure, but as far as I can tell, the code above should be running on a single entity. If I was running this code on all entities, sure a job would make sense. But again, I am running the code on a specific entity. I’m not clear why having more entities impacts perf in this case.

are you positive its only operating on one entity?

some things:
Its unclear how you are determining the entity for this system with your entityhelper class.
You should be using SystemBase over JobComponentSystem and ComponentSystem & EntityManager is a property of all three.
You can use RequireSingletonForUpdate() in OnCreate to ensure that there is only ever one of that entity when the system updates(and then use GetSingletonEntity in OnUpdate to access it).
I assume you aren’t updating OxygenSingletonSystem yourself either? Nothing in that looks like it should be taking as much time as it is but its still hard to gauge without seeing a bit more info. If you up the entities in scene to 10x or 100x the amount, does it also increase the ms of this one system?

Thanks for reporting this; this should not be happening. Can you file a bug report and attach your project as a repro case? Then we can have someone look into it.

  1. Here’s the code from the entity helper. It looks pretty vanilla to me:
        public static Entity GetPlayerEntity(ComponentSystem componentSystem)
        {
            Entity entity = Entity.Null;

            if (componentSystem.HasSingleton<PlayerEntityComponent>())
               entity = componentSystem.GetSingletonEntity<PlayerEntityComponent>();
            return entity;
        }
  1. I switched to using SystemBase and there’s no change in the perf
  2. No I am not updating the system elsewhere.
  3. I added the RequireSingletonForUpdate as you suggested. No change in perf.
  4. Yes, perf scales with the number of entities.

It sounds like you just have an very complicated way of writing Entities.ForEach.
This makes your code slow & hard to understand.

The right way of writing this code is to use

  1. SystemBase
  2. Use Entities.ForEach for iterating over a set of components & dynamic buffer
    In that configuration your Entities.ForEach code will actually be burst compiled which is what makes it actually fast.

It also avoids a bunch of indirect lookups you have right now, looking up from entity → Buffer & components.

On top of this it makes your code cleaner and not require to be singleton based. It will let you get rid of all of the early outs and checks against if the singleton exists or not. Right now this adds code complexity & lowers performance. On top of that, if there ends up being no Player, then the system will automatically skip running since no queries are matching.

1 Like

Something like this:

public class OxygenSystem : SystemBase
    { 
        protected override void OnUpdate()
        {
            float deltaTime = Time.DeltaTime;

            Entities.WithAll<PlayerEntityComponent>().ForEach((DynamciBuffer<ChangeStatQueueBufferElement> statChangeBuffer)) =>
            {
                //Rate of O2  usage
                //Needs to be moved to a table
                float  oxygenPerMinuteScale = 10.0f;
               
                //O2
                float deltaOxygen = -1 * oxygenPerMinuteScale * deltaTime;
    
                //Create a stat queue message
                ChangeStatQueueBufferElement changeStatQueueBufferElement = new ChangeStatQueueBufferElement()
                {
                    StatType = StatType.Oxygen,
                    Value = deltaOxygen
                };
    
                statChangeBuffer.Add(changeStatQueueBufferElement);

            }).Run();
        }
    }
1 Like

Joachim_Ante,
Switching to the foreach doesn’t fix the problem. Even when I cut the function down the bones:

    public class OxygenSingletonSystem : SystemBase
    {
        protected override void OnCreate()
        {
            this.RequireSingletonForUpdate<PlayerEntityComponent>();
        }
     
        protected override void OnUpdate()
        {
              Entities.WithAll<PlayerEntityComponent>().ForEach((ref DynamicBuffer<ChangeStatQueueBufferElement> statChangeBuffer) =>                {
                   // ChangeStatQueueBufferElement changeStatQueueBufferElement = new ChangeStatQueueBufferElement() { StatType = StatType.Oxygen, Value = deltaOxygen};
                    //statChangeBuffer.Add(changeStatQueueBufferElement);
                }).Run();
        }
    }

The perf for this code still scales based on the number of entities in my scene despite there being only one entity with the “PlayerEntityComponent.”

Its possible I’m doing something dumb here or elsewhere in the project but it’s not obvious to me.

Submitted. thanks for offering to take a look.

I thought it was known DynamicBuffers have huge overhead? There are also a lot of checks in the editor so it’s best to make a developer build and attach the profiler to it. These are your real timings then. It would also be helpful to post profiler timings because I don’t see anything really unusual going on with my buffers, although they could always be much faster of course. :slight_smile:

There’s no need for declaring entityManager and EntityHelper.GetEntityManager()
It’s a base property of all system bases.

It’s also not obvious for us if you ever clear the buffer correctly. For all we know you could be filling the memory endlessly.

Also, and most important. If you just need 1, consider using a IComponentData instead of a buffer and bonus points when you can work around structural changes without using add/removing the comp.

The real problem is that in this example EntityManager.GetBuffer seems to scale in the number of entities, which should never be the case (with or without safety checks, in the editor or the player).

I have only briefly looked at this so far, but there is one thing that could actually cause this behavior: When you call EntityManager.GetBuffer, you are trying to access data on the main thread. If there is any other system that writes to buffers of that type, it might be that that system scheduled a job that scales linearly in the number of entities and the call you are seeing as expensive must wait for that operation to finish to avoid a race condition. This will also happen in a player, because this is essential for the correctness of the program. This problem should be visible in the profiler, the GetBuffer call should cause a JobHandle.Complete in this case.

This seems like the most likely explanation for what you are seeing; I’d suggest taking a close look in the profiler to see whether that is the case. I’m taking your post to mean at the very least that the documentation for this function needs to mention this more clearly (if it mentions this at all so far).
If you want to avoid this problem, I’d suggest trying to scheduling the access to GetBuffer as well, e.g. by taking Joachim’s example and using Schedule instead of Run at the end.

2 Likes

They don’t. They are of course not as fast as having a fixed size buffer directly on a component, but compared to say std::vector it’s pretty safe to say that DynamicBuffers play in the same ballpark.

1 Like