#c# #unity3d #parallel-processing #jobs
#c# #unity3d #параллельная обработка #Вакансии
Вопрос:
Я пытаюсь оптимизировать работу с использованием IJobParallelFor в коде Unity: к сожалению, я столкнулся с ошибкой, подобной этой:
Система.Исключение IndexOutOfRangeException: индекс {0} находится вне ограниченного диапазона IJobParallelFor [{1} …{2}] в ReadWriteBuffer.
Я пытался использовать
[NativeDisableParallelForRestriction]
и
[NativeDisableContainerSafetyRestriction]
но без эффекта
[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 amp;amp; neighboursArray[neigbourIndex] >= 0 amp;amp;
colorsArray[neighboursArray[neigbourIndex]].b == 255)
{
voxelToColor[listIndex] = index;
listIndex ;
}
}
}
}
}
}
Ответ №1:
Afaik это исключение вызвано тем фактом, что IJobParallelFor
, как то же самое говорит, выполняется параллельно
Execute(int index)
будет выполняться один раз для каждого индекса от 0 до указанной длины. Каждая итерация должна быть независимой от других итераций (система безопасности применяет это правило для вас). Индексы не имеют гарантированного порядка и выполняются на нескольких ядрах параллельно.Unity автоматически разбивает работу на куски размером не менее предоставленного размера пакета и планирует соответствующее количество заданий на основе количества рабочих потоков, длины массива и размера пакета.
Это означает, что, допустим, у вас есть массив длины 10
, и вы говорите, что размер пакета может быть до 4
, тогда вы, вероятно, получите 3 параллельных блока заданий для индексов [0, 1, 2, 3]
, [4, 5, 6, 7]
и [8, 9]
. Поскольку каждый из этих 3 блоков, возможно, находится на другом ядре, они получают доступ только к соответствующему фрагменту NativeArray
(-ам). (Подробнее об этом здесь)
Вероятно, происходит то, что несколько ваших параллельных заданий пытаются получить доступ и записать в один и тот же индекс ваших выходных NativeArray
данных voxelToColor
и neighboursArray
. В частности, без выполнения остальных вычислений вы, безусловно, попытаетесь выполнить запись voxelToColor[0]
в каждом из ваших параллельных заданий, что не разрешено и не имеет для меня большого смысла.
В течение одного Execute
вызова вы можете выполнять запись только в указанный индекс.
Afaik сообщение должно дополнительно читать
ReadWriteBuffers
ограничены только чтением и записью элемента в индексе задания. Вы можете использовать стратегии двойной буферизации, чтобы избежать условий гонки из-за параллельного чтения и записи одних и тех же элементов из задания.
[ReadOnly]
помеченные массивы являются исключением, потому что здесь несколько параллельных обращений не могут повредить данные, пока вы только читаете.
Затем [NativeDisableContainerSafetyRestriction]
, если я правильно понимаю, решает условия гонки между различными заданиями и основным потоком.
Хотя вы, вероятно, хотите пойти с чем-то большим [NativeDisableParallelForRestriction]
, что, насколько я понимаю, отключает ограничение безопасности для параллельного доступа к индексам массива. Честно говоря, Unity API довольно скуп на них.
В качестве небольшого примера
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;
}
}
не должно вызывать никаких исключений. Но если вы закомментируете [NativeDisableParallelForRestriction]
, вы получите исключение, которое получаете.