Wow… Pretty amazing results. Do I even need to tell which is which? They are almost identical.

Blue - Setting a 1000 components twice on the main, and iterate over a 10,000,000 in a job.
Red - Creating and destroying a 1000 event entities on the main, and use them via ComponentDataFromEntity in a job. Number of existing target entities doesn’t matter, so imagine it’s also 10,000,000
This makes a separate entity based event system very much viable, which is great since it might be a better design. Except the whole create the entity, connect to the target and then get data from its target process requires a lot of extra steps and buffering when you want to use batching. If API gets better at it, this could be a great way though.
Also, need to note that it is only that fast for the entities that are living only one frame because they are deleted by archetype all at once.
EDIT: I forgot burst while using the event entities, and with burst, it almost disappears from the worker thread, so the batched event entities are actually faster in the benchmark.

The code.
using Unity.Entities;
using Unity.Burst;
using Unity.Jobs;
using Unity.Collections;
using Unity.Transforms;
using Unity.Mathematics;
using UnityEngine;
struct TestIterationComponent : IComponentData { public int Value; }
struct TestEventComponent : IComponentData { public Entity Target; }
struct TestTargetComponent : IComponentData { public int i; }
[UpdateBeforeAttribute(typeof(CreateEntitySystem))]
public class IterateSystem : JobComponentSystem
{
struct Targets
{
public ComponentDataArray<TestIterationComponent> Component;
public EntityArray Entities;
}
[Inject] Targets components;
override protected void OnCreateManager()
{
for (int i = 0; i < 1000000; i++)
{
EntityManager.CreateEntity(ComponentType.Create<TestIterationComponent>());
}
}
[BurstCompile]
struct Job : IJobProcessComponentData<TestIterationComponent>
{
public void Execute(ref TestIterationComponent c)
{
if (c.Value != 0)
{
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
for (int i = 0; i < 2000; i++)
{
EntityManager.SetComponentData<TestIterationComponent>(components.Entities[i], new TestIterationComponent { Value = 1 });
}
var job = new Job { };
return job.Schedule(this, inputDeps);
}
}
public class CreateEntitySystem : ComponentSystem
{
EntityArchetype eventArvhetype;
struct Targets
{
public ComponentDataArray<TestTargetComponent> T;
public EntityArray Entities;
}
[Inject] Targets targets;
override protected void OnCreateManager()
{
eventArvhetype = EntityManager.CreateArchetype(typeof(TestEventComponent));
var newTargets = new NativeArray<Entity>(1000000, Allocator.Temp);
EntityManager.CreateEntity(EntityManager.CreateArchetype(typeof(TestTargetComponent)), newTargets);
for (int i = 0; i < 1000000; i++)
{
EntityManager.SetComponentData<TestTargetComponent>(newTargets[i], new TestTargetComponent { i = 1 });
}
newTargets.Dispose();
}
protected override void OnUpdate()
{
var targetEntities = new NativeArray<Entity>(1000, Allocator.Temp);
for (int i = 0; i < 1000; i++)
{
targetEntities[i] = targets.Entities[i];
}
var events = new NativeArray<Entity>(1000, Allocator.Temp);
EntityManager.CreateEntity(eventArvhetype, events);
for (int i = 0; i < 1000; i++)
{
EntityManager.SetComponentData<TestEventComponent>(events[i], new TestEventComponent { Target = targetEntities[i] });
}
events.Dispose();
targetEntities.Dispose();
}
}
[UpdateAfter(typeof(CreateEntitySystem))]
public class UseEntitiesSystem : JobComponentSystem
{
[Inject] ComponentDataFromEntity<TestTargetComponent> componentDataFromEntity;
ComponentGroup g;
override protected void OnCreateManager()
{
g = EntityManager.CreateComponentGroup(typeof(TestEventComponent));
}
[BurstCompile]
struct Job : IJobProcessComponentDataWithEntity<TestEventComponent>
{
[ReadOnly] public ComponentDataFromEntity<TestTargetComponent> componentDataFromEntity;
public void Execute(Entity e, int i, ref TestEventComponent c)
{
var d = componentDataFromEntity[c.Target];
if (d.i == 0)
{
d.i = 1;
}
else
{
d.i = 0;
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var job = new Job
{
componentDataFromEntity = componentDataFromEntity
};
return job.Schedule(this, inputDeps);
}
}
[UpdateAfter(typeof(UseEntitiesSystem))]
public class DestroyEntitySystem : ComponentSystem
{
ComponentGroup g;
override protected void OnCreateManager()
{
g = EntityManager.CreateComponentGroup(typeof(TestEventComponent));
}
protected override void OnUpdate()
{
EntityManager.DestroyEntity(g);
}
}
Now I’m gonna try to find a way to make event entities pretty and usable in the code. For state switching setting components is still better of course. For things like JustSpawned, IncomingDamage, GameOver, the event entities are the best. Especially Damage in this case since you can add as many instances as you wish.
The difference is more in favor of event entities when there are more of them.
- Setting 10,000 and iterating costs 9ms because we are setting twice
- Create>Set>Destroy 1.5ms
Actually doesnt make sense, I would expect 9 and 4.5 ms at least.