Search code examples
c#unity-game-engineparallel-processingunity-containerjobs

Why is NativeArray needed to obtain return values from Unity's Job System?


Using the code block below as reference, a single update provides the following results:

intReturn = 0

inputArrayReturn = 1

internalArrayReturn = 1

The only value that makes sense to me here is internalArrayReturn.

Why doesn't intReturn equal 1? What is going on to cause the stuct's value to not change even though during execution it should have had 1 added to it? If it is simply a fundamental difference between NativeArray and int, could someone please explain or point me to a reference that will help me understand this better?

Also, why doesn't inputArrayReturn equal 0? NativeArray is a struct, so shouldn't it be treated as a copy of values rather than a reference when the line testArray = testArrayResults executes? If this was the case, then inputArrayReturn would stay equal to 0 rather than being updated to the same value contained in testJob.testArray.

using UnityEngine;
using Unity.Jobs;
using Unity.Collections;

public class ParallelTesting : MonoBehaviour
{
    void Update()
    {
        NativeArray<int> testArrayResults = new NativeArray<int>(1, Allocator.TempJob);

        TestJob testJob = new TestJob
        {
            testInt = 0,
            testArray = testArrayResults
        };

        JobHandle testJobHandle = testJob.Schedule();
        testJobHandle.Complete();

        int intReturn = testJob.testInt;
        int inputArrayReturn = testArrayResults[0];
        int internalArrayReturn = testJob.testArray[0];

        testArrayResults.Dispose();
    }

    public struct TestJob : IJob
    {
        public int testInt;
        public NativeArray<int> testArray;

        public void Execute()
        {
            testInt += 1;
            testArray[0] = testInt;
        }
    }
}

Solution

  • No expert but I'ld say NativeArray is specifically designed to be thread save and basically a shared memory between the Unity main thread and the job system/runner.

    And yes NativeArray is a struct but in fact if you look at the source code it basically only is a wrapper holding a pointer to the actual native buffer in the memory.

        public unsafe struct NativeArray<T> : IDisposable, IEnumerable<T>, IEquatable<NativeArray<T>> where T : struct
        {
            [NativeDisableUnsafePtrRestriction]
            internal void*                    m_Buffer;
            internal int                      m_Length;
    
            internal int                      m_MinIndex;
            internal int                      m_MaxIndex;
            
            ...
    

    That's why both

    int inputArrayReturn = testArrayResults[0];
    int internalArrayReturn = testJob.testArray[0];
      
    

    access the same external memory via the same pointer and therefore return both the same and correct value.

    The "normal" int however is not necessarily thread safe since it is in no external memory allocated (see Heap and Stack) and your Unity main thread doesn't know that the job internally changed it's value while it was on a different thread/core.