Poor performance in simple system.

I have poor performancence in very simple system that spawns cubes (ecs prefabs) from a spawner. I am using authoring workflow. Longer it works slower it gets. I do not understand why CommandBuffer is getting slower and slower. spawning rate does not change.

using System;
using Unity.Entities;
using Unity.Mathematics;

[Serializable]
public struct Spawner : IComponentData
{
    public Entity prefab;
    public float3 size;
    public float spawnRate;
    public float time;
}
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;

[DisallowMultipleComponent]
[RequiresEntityConversion]
public class SpawnerAuthoring : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
{
    public GameObject prefab;
    public Vector3 size;
    public float spawnRate;


    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        dstManager.AddComponent(entity, ComponentType.ReadWrite<Spawner>());
        dstManager.SetComponentData(entity, new Spawner()
        {
            prefab = conversionSystem.GetPrimaryEntity(prefab),
            spawnRate = spawnRate,
            time = 1f / spawnRate,
            size = size
        });
    }

    public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(prefab);
    }

    public void OnDrawGizmosSelected()
    {
        Gizmos.DrawCube(transform.position, size);
    }
}
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
using Random = Unity.Mathematics.Random;

public class Spawner2System : JobComponentSystem
{
    private Random rand;
    private BeginSimulationEntityCommandBufferSystem barrier;

    protected override void OnCreate()
    {
        rand = new Random(42);
        barrier = World.Active.GetOrCreateSystem<BeginSimulationEntityCommandBufferSystem>();
    }

    struct Spawner2SystemJob : IJobForEachWithEntity<Spawner>
    {
        public Random rand;
        public EntityCommandBuffer.Concurrent PostUpdateCommands;
        public float deltaTime;


        public void Execute(Entity e, int index, ref Spawner spawner)
        {
            spawner.time -= deltaTime;
            if (spawner.time <= 0)
            {
                var abs = math.abs(spawner.time);

                int count = (int) (abs / (1 / spawner.spawnRate) + 1);

                var entity = PostUpdateCommands.Instantiate(index, spawner.prefab);
                for (int i = 0; i < count; i++)
                {
                    PostUpdateCommands.SetComponent(index, entity, new Translation()
                    {
                        Value = rand.NextFloat3(-spawner.size, spawner.size)
                    });
                }

                spawner.time += 1f / spawner.spawnRate;
            }
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        var job = new Spawner2SystemJob()
        {
            deltaTime = Time.deltaTime,
            PostUpdateCommands = barrier.CreateCommandBuffer().ToConcurrent(),
            rand = rand
        };

        var handle = job.Schedule(this, inputDependencies);

        barrier.AddJobHandleForProducer(handle);

        return handle;
    }
}

Memory leak maybe?

[edit] They seem to show up as GameObjects, not Entities. Are you converting to entities?

Because any Create/Destroy command has to rebuilt the whole chunk structure. Create/Destroy and to a lesser extent, Set, should be avoided at all times when dealing with thousands of entities. One way to go around this is to pool your entities or to batch create your entities.

Have you looked at how many entities are actually spawned each frame? My suspicion is that you will spawn more and more entities over time. Even with a constant spawner.spawnRate, this can happen in your scenario. Specifically, consider this part of your code (with some numbering that I added):

        public void Execute(Entity e, int index, ref Spawner spawner)
        {
            spawner.time -= deltaTime; // (1)
            if (spawner.time <= 0)
            {
                var abs = math.abs(spawner.time);
                int count = (int) (abs / (1 / spawner.spawnRate) + 1); // (2)
                var entity = PostUpdateCommands.Instantiate(index, spawner.prefab);
                for (int i = 0; i < count; i++)
                {
                    PostUpdateCommands.SetComponent(index, entity, new Translation()
                    {
                        Value = rand.NextFloat3(-spawner.size, spawner.size)
                    });
                }
                spawner.time += 1f / spawner.spawnRate; // (3)
            }
        }

What will happen:

  • at (1) you will eventually generate a number smaller than zero.
  • the formula at (2) means that the number of entities spawned is essentially proportional to the absolute value of spawner.time, i.e. count = 1 + (int) abs * spawner.spawnRate
  • at (3) you increase spawner.time, but if the deltaTime (the time taken for the last frame) is big enough, then spawner.time can still be very negative, which will mean that you spawn even more entities in the next frame, which will make that slower, leading to more entities in the frame after that etc. - you’re essentially stuck in a feedback loop and deltaTime gets bigger and bigger while spawn.time gets more and more negative.
1 Like

You are right. I made a mistake in this line. I should multiply it by the number of entities spawned.

I know that this code is not very optimal, I just want to create simple spawn to spawn entities with new physics components to mess around with it.

I saw that in the debugger to. But I am using conversion code is included. Maybe I am doing something wrong, if they were GameObjects they should be visible in the hierarchy. I do not know why components do not show in the inspector.

That because

var entity = PostUpdateCommands.Instantiate(index, spawner.prefab);

Which during entity instantiation, prefab entity name is copied as well.
So you need explicitly change name of entity if you like.

1 Like

You’re welcome :slight_smile: My comment was less about whether the code is optimal. I actually think that this line is the reason why you have performance problems because it might lead to an explosion in the number of entities.