struct StorageErrandJob : IJobForEachWithEntity<FetchReceiver> {
public EntityCommandBuffer.Concurrent commandBuffer;
public void Execute(Entity entity, int index, [ReadOnly] ref FetchReceiver fetchReceiver) {
....
commandBuffer.RemoveComponent<StorageInternalTag>(index, entity);
}
}
It works, but my concern is that I am using index incorrectly. Index in this code is index of the entity not job index. I am passing it RemoveComponent() as it is job index. It could be only 128 jobs, number of entites could be much more then 128. So it seems like this code would work only with small number of entities.
Am I missing something? What is correct way to use EntityCommandBuffer.Concurrent inside IJobForEachWithEntity?
internal void InitConcurrentAccess()
{
if (m_ThreadedChains != null)
return;
// PERF: It's be great if we had a way to actually get the number of worst-case threads so we didn't have to allocate 128.
int allocSize = sizeof(EntityCommandBufferChain) * JobsUtility.MaxJobThreadCount;
m_ThreadedChains = (EntityCommandBufferChain*) UnsafeUtility.Malloc(allocSize, JobsUtility.CacheLineSize, m_Allocator);
UnsafeUtility.MemClear(m_ThreadedChains, allocSize);
}
As I understand m_ThreadedChains are used inside EntityCommandBuffer.Concurrent to make it threadsafe by associating one chain with one job. Is this correct?
I belive that IJFE runs a job for each chunk too. But I am also pretty sure that index is entity index.
Take a look at this code from IJobForEach.gen.cs
var chunk = chunks[blockIndex];
int beginIndex = entityIndices[blockIndex];
var count = chunk.Count;
....
for (var i = 0; i != count; i++)
{
jobData.Data.Execute(ptrE[i], i + beginIndex, ref UnsafeUtilityEx.ArrayElementAsRef<U0>(ptr0, i), ref UnsafeUtilityEx.ArrayElementAsRef<U1>(ptr1, i));
}
It shows that index is some base beginIndex plus i. i takes values from 0 to number entities in chunk.
I am not an expert in multithreaded code but things are getting mixed up here. This 128 limit comes from optimizing concurrent access (cachelines, threads) it is at the moment over allocating because they don’t determine the maximum theoretical parallel threads (which should be dependent on your cores).
I think [NativeSetThreadIndex] is for native containers I am not 100% sure that it works inside a job. I will do a test in a moment. Update: [NativeSetThreadIndex] works great inside jobs!
Using CG batch API is really great advice. I will try it.
I digged source code little bit more and here is what I found.
jobIndex is not threadIndex! Isn’t this a surprise? EntityCommandBuffer.Concurrent gets threadIndex as other containers do, it just declares [NativeSetThreadIndex] internal int m_ThreadIndex; . What is jobIndex if it is not the threadIndex? It looks like EntityCommandBuffer commands are executed not in the order they were recorded. Sorting operation is applyed to the commands before execution, this is where jobIndex is used. This sorting operation isn’t trivial - it will keep order of commands with the same jobIndex, but groups of commands with the same jobIndex are sorted in order of ascending jobIndex.