So before today’s update, I could use ReadOnly BufferFromEntity in IJobChunk. After upgrading to 0.1.0-preview, it gives error:
If I remove the ReadOnly attr, it gives another error:
Here’s the full code that used to work in 0.0.12-preview.33:
[BurstCompile]
struct FillViewers : IJobChunk {
[ReadOnly] public ArchetypeChunkEntityType EntityType;
public ArchetypeChunkBufferType<SyncedEntityElement> SyncedEntityElement;
[ReadOnly] public BufferFromEntity<ViewerEntityElement> ViewerEntityBFE;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) {
var entities = chunk.GetNativeArray(EntityType);
var syncedEntityBufferAccessor = chunk.GetBufferAccessor(SyncedEntityElement);
for (int i = 0; i < syncedEntityBufferAccessor.Length; i++) {
var entity = entities[i];
var syncedEntities = syncedEntityBufferAccessor[i];
for (int j = 0; j < syncedEntities.Length; j++) {
var viewers = ViewerEntityBFE[syncedEntities[j]];
viewers.Add(entity);
}
}
}
}
You are writing (as you use the function Add()) in the buffer, and this can cause some problems when doing parallel jobs, if you want to fix that issue you can set the job as single threaded, or if you are sure your code is 100% safe then add the attribute [NativeDisableParallelForRestriction] to your ‘BufferFromEntity’ variable.
Thanks! Yes in my specific case, safety is no concern. So either [NativeDisableParallelForRestriction] or [NativeDisableContainerSafetyRestriction] will do. I guess I just find it weird ReadOnly BufferFromEntity is being enforced now and not before.
[DisableParallelForRestriction]
public BufferFromEntity ViewerEntityBFE;
If you can gurantee for your game that no two buffers will be accessed by the parallel chunks being processed. Essentially you need to be certain there are no two ViewerEntityElement that point to the same entity in your game.
If you can’t gurantee it, then you can simply use ScheduleSingle instead of Schedule on the job. thus it will not run the job parallel thus it is safe to access any element by entity.
None of this is a good solution… The real issue here, is that you are looking at the problem the wrong way. The best solution is to express it differently…
The Safe by default & performant by default approach is to change it to a pull model. The destination data knows where it can copy it’s data from. Reading can always be done safely in parallel. So that approach has the advantage of being inherently safe to multithread.
Additionally this approach reduces false sharing for the multiple jobs running in parallel.
This can in fact be a massive performance problem if you do not invert how you try to solve the problem.
I think in my previous example, it would be to invert the roles of ViewerEntityElement and SyncedEntityElement. @Joachim_Ante_1 makes a very good point about the RAM cache caveat, I never really thought about that.
However, in my case, if you simply invert the roles (start with ViewerEntityElement and calc/pull the necessary data), the perf will go from O(n) to O(n^2) because pulling this way would require looking at all entities against all entities.