When should I use which value (None
, Invalid
, Temp
, TempJob
, Persistent
, FirstUserIndex
, AudioKernal
)
How does each Allocator
affect allocation and the lifespan of the NativeArray
in implementation?
I can't figure out how the lifespans/implementations differ for each Allocator
value when instantiating a NativeArray
. I've checked the docs and IntelliSense to no avail, besides being shown which Enum
values are available.
In-depth look at allocators:
Allocator.Temp
Offers fastest allocation time. Is valid for a single frame. No need to call Dispose()
.
Can't be assigned to field of a job struct (because job's lifetime can be over 1 frame) but can be allocated by a job execution code.
struct AJob : IJob
{
void IJob.Execute ()
{
var tempArray = new NativeArray<int>( 128 , Allocator.Temp );
for( int i=0 ; i<tempArray.Length ; i++ )
{
/* does something useful with it */
}
// tempArray.Dispose() call is optional as Temp will deallocate automagically
}
}
Allocator.TempJob
Fast, temporary allocations for job struct fields.
public class LetsScheduleAJob : MonoBehaviour
{
[SerializeField] int[] _inputs = new int[]{ 1 , 2 , 3 , 4 , 5 , 6 , 7 };
public JobHandle Dependency;
void Update ()
{
Dependency.Complete();// makes sure previously-scheduled job is completed by now
var temporaryData = new NativeArray<int>( _inputs , Allocator.TempJob );
Dependency = new SumUpJob{
NumbersToSumUp = temporaryData ,
}.Schedule();
temporaryData.Dispose( Dependency );// deallocates on job completion
}
}
struct SumUpJob : IJob
{
public NativeArray<int> NumbersToSumUp;
void IJob.Execute ()
{
int sum = 0;
for( int i=0 ; i<NumbersToSumUp.Length ; i++ )
sum += NumbersToSumUp[i];
Debug.Log($"sum: {sum}");
}
}
Allocator.Persistent
Slow allocation. No lifetime restrictions. Must call Dispose()
to free (memory leak otherwise). Ideal for data that needs to always exist for a system to function.
public class LetsScheduleAJob : MonoBehaviour
{
[SerializeField] int[] _inputs = new int[]{ 1 , 2 , 3 , 4 , 5 , 6 , 7 };
NativeArray<int> _data;
public JobHandle Dependency;
void Awake ()
{
_data = new NativeArray<int>( _inputs.Length , Allocator.Persistent );
_data.CopyFrom( _inputs );
}
void OnDestroy ()
{
Dependency.Complete();
if( _data.IsCreated ) _data.Dispose();// I (class) allocated this array so (only) I Dispose it
}
void Update ()
{
Dependency.Complete();// makes sure previously-scheduled job is completed by now
_data.CopyFrom( _inputs );
Dependency = new SumUpJob{
NumbersToSumUp = _data ,
}.Schedule();
}
}
Allocator.None
Allocator.None
is for these rare cases where you need to say "no allocation happened here" to create a valid NativeArray
of which lifetime is decided elsewhere. For example, to access managed memory as if it is just another NativeArray
:
void* ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(managedArray, out gcHandle);
var nativeArray = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(ptr, managedArray.Length, Allocator.None);
source: https://gist.github.com/andrew-raphael-lukasik/812e152f95e38cc13c44e5040c5739bc
Invalid
Signals allocation failure. Don't use.
FirstUserIndex
No idea. Don't use.
@derHugo wrote:
FirstUserIndex
is just another check index, you can define custom allocators (see AllocatorManager.Register), you wouldn't pass this one in yourself though, this is internally handled
AudioKernel
No idea. Don't use.
@derHugo wrote: Seems to be very use case specific for jobs related to the DOTS DSPGraph