Search Unity

Parallel Writes to a DynamicBuffer

Discussion in 'Entity Component System' started by PublicEnumE, Jul 8, 2019.

  1. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    729
    I’ve been away for a few months, and I’m getting caught up with the recent changes. Please help me remember:

    Is it possible to have multiple, parallel jobs write (add) to a single DynamicBuffer? Nothing needs to read during that time, but I’m pretty sure parallel adding would cause race conditions (even with the attribute that allows for parallel access). Is my understanding of this flawed?

    Thanks for any help!
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Yep, you can't do parallel writes.
     
  3. Kmsxkuse

    Kmsxkuse

    Joined:
    Feb 15, 2019
    Posts:
    306
    Establish the size of the dynamic buffer using ResizedUninitialized, convert the DB to a native array using AsNativeArray (essentially free according to Unity) in the job scheduling, then proceed as normal with a NativeArray<T> in the job itself. If you're adding things to the DB in the job, use the concurrent versions of NativeMultiHashMap/NativeQueue/NativeHashMap then add to the DB in a single threaded job.

    AsNativeArray will mirror the changes you do to the NativeArray in the Job with the Dynamic Buffer on the entity. And you dont need to dispose of the AsNativeArray because the NativeArray created is simply an interface for the Dynamic Buffer it was created from. The Dynamic Buffer is handled automatically by the current world manager.

    In fact, unless you're resizing the DB like a list, Unity encourages that all modifications to a Dynamic Buffer are to be done through the AsNativeArray interface.

    This is what I concluded from the past couple of weeks of experimenting and digging through forums. Anyone can save 10 mins of looking through documentation with 10 hours of Google searches.
     
    bb8_1, tarahugger and PublicEnumE like this.
  4. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    729
    @Kmsxkuse: Thank you very much for this advice. It's exactly what I was looking for.

    One question about this quote:

    Let's say I already have the DB sized appropriately. It will never need to resize. I've used AsNativeArray to get a NativeArray<T> representation of the DB. Now, I'm in a Job, and I'm only assigning values to existing indices of that NativeArray. I'm not Adding/Removing things to the DB, or doing anything that would require it to be resized, or reordered. All I'm doing is:

    myDBNativeArray[i] = new MyBufferElement();


    ..where 'i' is guaranteed to be valid.

    Will I still need to do this:

    I'm hoping that I don't, and that as long as two Jobs are never assigning to the same indices, I can just do it all inside of the one Job. Please advise!

    Thank you. :)
     
  5. Kmsxkuse

    Kmsxkuse

    Joined:
    Feb 15, 2019
    Posts:
    306
    Sorry for such a late replay, fall classes are starting back up again and once again my daily schedule is in absolute chaos.

    Modifications to a .AsNativeArray() given NativeArray within a job is exactly the same as any regular NativeArray. The same rules apply to changing a DynamicBuffer as changing a NativeArray.

    Essentially, so long as MyBufferElement() is a struct consisting of blittable types (int, float, or combinations of them) and 'i' is within established length, you can modify the DynamicBuffer within a job.

    Same applies to using a DynamicBuffer.AsNativeArray() in a parallel job. [WriteOnly] or the restriction lifting tag (forgot the exact text vomit), then use it as a native array. Which it is, just attached to an entity. Checking for race conditions when you dont use [WriteOnly] is the same as using NativeArrays with the restriction lifted, up to you.
     
    PublicEnumE likes this.