NativeMultiHashMap Concurrent Performance

I’d been using NativeMultiHashMap to gather together spell occurrences according by target entity, but I noticed that NativeMultiHashMap.ParallelWriter uses atomic operations, are atomic operations too bad in this case(assuming that it’ll be dozens of this systems running, one per each spell component) ? if it does, is there a better way to do it?

Job Structures

   [BurstCompile]
   [ExcludeComponent(typeof(SpellHeader))]
   public struct GatherBreakingSpellJob : IJobForEachWithEntity<SpellState<TSpell, TTargetComponent>>
   {
      [WriteOnly]
      public NativeMultiHashMap<Entity, SpellEntry>.ParallelWriter SpellEntries;

      public void Execute(Entity Entity, int Index, ref SpellState<TSpell, TTargetComponent> SpellState)
      {
         SpellEntries.Add(SpellState.TargetEntity, new SpellEntry
         {
            Entity = Entity,
            Spell = SpellState.Spell,
            IsCasting = false
         });
      }
   }

   [BurstCompile]
   public struct GatherCastingSpellJob : IJobForEachWithEntity<SpellHeader, TSpell>
   {
      [WriteOnly]
      public NativeMultiHashMap<Entity, SpellEntry>.ParallelWriter SpellEntries;

      public void Execute(Entity Entity, int Index, [ReadOnly] ref SpellHeader SpellHeader, [ReadOnly] ref TSpell SpellComponent)
      {
         SpellEntries.Add(SpellHeader.TargetEntity, new SpellEntry
         {
            Entity = Entity,
            Spell = SpellComponent,
            IsCasting = true
         });
      }
   }

   [BurstCompile]
   public struct SpellJob : IJobNativeMultiHashMapVisitKeyValue<Entity, SpellEntry>
   {
      [NativeDisableParallelForRestriction]
      public ComponentDataFromEntity<TTargetComponent> TargetComponentDataFromEntity;

      [WriteOnly]
      public EntityCommandBuffer.Concurrent EntityCommandBuffer;

      [NativeSetThreadIndex]
      int m_ThreadIndex;

      public void ExecuteNext(Entity TargetEntity, SpellEntry SpellEntry)
      {
         bool IsCasting = SpellEntry.IsCasting;

         if (!TargetComponentDataFromEntity.Exists(TargetEntity))
         {
            EntityCommandBuffer.DestroyEntity(m_ThreadIndex, SpellEntry.Entity);

            if (!IsCasting)
            {
               EntityCommandBuffer.RemoveComponent(m_ThreadIndex, SpellEntry.Entity, typeof(SpellState<TSpell, TTargetComponent>));
            }

            return;
         }

         var TargetComponent = TargetComponentDataFromEntity[TargetEntity];
         ref var Spell = ref SpellEntry.Spell;

         if (IsCasting)
         {
            Spell.Cast(ref TargetComponent);

            EntityCommandBuffer.AddComponent(m_ThreadIndex, SpellEntry.Entity, new SpellState<TSpell, TTargetComponent>
            {
               Spell = Spell,
               TargetEntity = TargetEntity
            });
         }
         else
         {
            Spell.Break(ref TargetComponent);

            EntityCommandBuffer.RemoveComponent(m_ThreadIndex, SpellEntry.Entity, typeof(SpellState<TSpell, TTargetComponent>));
         }

         TargetComponentDataFromEntity[TargetEntity] = TargetComponent;
      }
   }

Switching to ScheduleSingle helped me with performance. No Concurrent writing needed.

1 Like

this is my approach now, thxx :smile:

No problem, it just frees all the other worker threads of unnecessary workload :slight_smile:

1 Like