2

I try to optimize pefrormance using IJobParallelFor in Unity code: Unfortunatelly I met error likie this:

System.IndexOutOfRangeException: Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.

I tried to use

[NativeDisableParallelForRestriction] and

[NativeDisableContainerSafetyRestriction]

but without effect

[BurstCompile(CompileSynchronously = true)]

public struct DilationJob : IJobParallelFor
{
    
[ReadOnly]
public NativeArray<Color32> colorsArray;

public NativeArray<int> voxelToColor;
public int kernelSize;
public NativeArray<int> neighboursArray;
public int cubeNumber;


public void Execute(int index)
{   
    int dimx = 512;
    int  dimy = 512; 
    
    //int[] neighboursArray = new int[kernelSize * kernelSize * kernelSize];
    int listIndex = 0;
    
        for (int i = -1; i < kernelSize - 1; i++)
        {
            for (int j = -1; j < kernelSize - 1; j++)
            {
                for (int k = -1; k < kernelSize - 1; k++)
                {
                    int neigbourIndex = (i + 1) * (j + 1) * kernelSize + (j + 1) * kernelSize + (k + 1);

                    if (neigbourIndex < 0)
                    {
                        neigbourIndex = 0;
                    }

                    neighboursArray[neigbourIndex] =
                        index + k * dimx * dimy + j * dimx + i;

                    if (neighboursArray[neigbourIndex] < colorsArray.Length && neighboursArray[neigbourIndex] >= 0 &&
                        colorsArray[neighboursArray[neigbourIndex]].b == 255)
                    {
                        voxelToColor[listIndex] = index;
                        listIndex++;
                    }
                }
            }
        }
}

}

1 Answers1

4

Afaik this exception is caused by the fact that an IJobParallelFor as the same says is executed in parallel

Execute(int index) will be executed once for each index from 0 to the provided length. Each iteration must be independent from other iterations (The safety system enforces this rule for you). The indices have no guaranteed order and are executed on multiple cores in parallel.

Unity automatically splits the work into chunks of no less than the provided batchSize, and schedules an appropriate number of jobs based on the number of worker threads, the length of the array and the batch size.

What this means is lets say you have an array of length 10 an you say the batchSize can be up to 4 then you will probably end up with 3 parallel job chunks for the indices [0, 1, 2, 3], [4, 5, 6, 7] and [8, 9]. Since each of these 3 chunks is possibly on a different kernel they only get access to their according chunk of the NativeArray(s). (More on that here)

What probably happens is that multiple of your parallel jobs try to access and write to the same index of your output NativeArrays voxelToColor and neighboursArray. In specific without doing the rest of calculation you definitely will possibly try to write to voxelToColor[0] in each and every of your parallel jobs which is neither allowed nor makes a lot of sense to me.

Within one Execute call you are restricted to only write to the given index.

Afaik the message should further read

ReadWriteBuffers are restricted to only read & write the element at the job index. You can use double buffering strategies to avoid race conditions due to reading & writing in parallel to the same elements from a job.

[ReadOnly] tagged arrays are the exception because here multiple parallel accesses can't corrupt the data as long as you only read.


Then the [NativeDisableContainerSafetyRestriction] if I understand correctly solves race conditions between different jobs and the main thread.

While what you probably want to go with is more [NativeDisableParallelForRestriction] which as far as I understand disables the security restriction for parallel access to array indices. To be honest the Unity API is quite spare on these.

As a little example

public class Example : MonoBehaviour
{
    public Color32[] colors = new Color32[10];

    private void Awake()
    {
        var job = new DilationJob()
        {
            colorsArray = new NativeArray<Color32>(colors, Allocator.Persistent),
            voxelToColor = new NativeArray<int>(colors.Length, Allocator.Persistent)
        };

        var handle = job.Schedule(colors.Length, 4);

        handle.Complete();

        foreach (var i in job.voxelToColor)
        {
            Debug.Log(i);
        }

        job.colorsArray.Dispose();
        job.voxelToColor.Dispose();
    }
}

[BurstCompile(CompileSynchronously = true)]
public struct DilationJob : IJobParallelFor
{
    [ReadOnly] public NativeArray<Color32> colorsArray;

    [NativeDisableParallelForRestriction]
    public NativeArray<int> voxelToColor;

    public void Execute(int index)
    {
        voxelToColor[index] = colorsArray[index].a;

        if (index + 1 < colorsArray.Length - 1) voxelToColor[index + 1] = 0;
    }
}

should not throw any exception. But if you comment out the [NativeDisableParallelForRestriction] you will get the exception you are getting.

derHugo
  • 83,094
  • 9
  • 75
  • 115