Hello.
Why are jobs only allowed to allocate temp memory (as far as UnsafeUtility.Malloc is concerned)?
Is it a hard limitation or something that could be disabled by circumventing default safety features in some way?
Afaik, it’s already possible to call any allocator through native plugins, even in burst compiled jobs.
If you allocate persistant container in a job, how to you get the ‘pointer’ to that container back to the main thread to be able to deallocate it later ?
I see what you mean. Unity’s containers are a managed type in c#'s type system so the following isn’t possible for them:
Job:
[BurstCompile]
public unsafe struct MallocTest : IJob
{
[NativeDisableUnsafePtrRestriction] public IntPtr* ptr;
public void Execute()
{
ptr[0] = (IntPtr)UnsafeUtility.Malloc(8, 0, Allocator.Persistent);
}
}
Usage:
unsafe
{
var arr = (IntPtr*)UnsafeUtility.Malloc(sizeof(IntPtr), UnsafeUtility.AlignOf<IntPtr>(), Allocator.Persistent);
var j = new MallocTest() { ptr = arr };
Debug.Log(j.ptr[0].ToInt64());
j.Schedule().Complete();
Debug.Log(j.ptr[0].ToInt64());
UnsafeUtility.Free((void*)j.ptr[0], Allocator.Persistent);
UnsafeUtility.Free(arr, Allocator.Persistent);
}
What confused me was that when you attempt to create persistent containers, the error raised claims that jobs can allocate only temp memory. And, if I recall correctly, Unity’s native containers use UnsafeUtility internally. From that, I inferred that there would be additional caveats regarding UnsafeUtility in jobs.
I guess the main reason is, that persistent allocations are very very slow as they need full thread synchronization. Temp allocations are separated per thread and need no synchronization.
Unity’s containers (eg UnsafeList) use AllocatorManager to do persistent allocations in jobs. Can’t give you any details, didn’t require it so far.
The persistent safe containers can’t be allocated because they require a managed DisposeSentinel and the safety system.
Exactly. This is still a source of confusion in 2021.
To reiterate: You can allocate Persistent memory from jobs with UnsafeUtility.Malloc
using UnityEngine;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using IntPtr = System.IntPtr;
public class MallocTester : MonoBehaviour
{
unsafe void Start ()
{
Debug.Log($"Test start [{System.DateTime.Now.ToShortTimeString()}]" );
IntPtr* intptr = (IntPtr*) UnsafeUtility.Malloc( UnsafeUtility.SizeOf<IntPtr>() , UnsafeUtility.AlignOf<IntPtr>() , Allocator.Persistent );
*intptr = new IntPtr(0);// clears allocated memory
var job = new MallocTest{ Ptr = intptr };
job.Schedule().Complete();
Debug.Log($"\tFree new job malloc:\t<b>{(long)(*job.Ptr).ToPointer()} (0x{System.Convert.ToString((long)(*job.Ptr).ToPointer(),16).ToUpper()})</b>" );
UnsafeUtility.Free( (*job.Ptr).ToPointer() , Allocator.Persistent );
Debug.Log($"\tFree original malloc:\t<b>{(long)intptr} (0x{System.Convert.ToString((long)intptr,16).ToUpper()})</b>" );
UnsafeUtility.Free( intptr , Allocator.Persistent );
}
[Unity.Burst.BurstCompile]
public unsafe struct MallocTest : IJob
{
[NativeDisableUnsafePtrRestriction] public IntPtr* Ptr;
public void Execute ()
{
void* newAlloc = UnsafeUtility.Malloc( UnsafeUtility.SizeOf<float>() * 128 , UnsafeUtility.AlignOf<float>() , Allocator.Persistent );
*Ptr = (IntPtr) newAlloc;
Debug.Log($"\tAllocated in-job:\t\t<b>{(long)newAlloc}</b>");
}
}
}
I am facing this issue in Unity 2022.3.28 version with URP. Does anyone have any idea why its happening or do I need to change the version? I have tried in 2022.3.20 and 2022.3.24.
In 1.4 is UnsafeUtility.Malloc Persistent allocations supported in a burst compiled job?
Assuming allocating in a job is allowed; something like this.
//Called when needed.
[BurstCompile]
public unsafe partial struct AllocBytesJob : IJobEntity
{
public void Execute(ref SaveOperationData save)
{
if (save.MallocPTR == null)
{
//Create data
save.MallocPTR = UnsafeUtility.Malloc(64, UnsafeUtility.AlignOf<byte>(), Allocator.Persistent);
UnsafeUtility.MemClear(save.MallocPTR, 64);
}
else
{
//Reuse data
UnsafeUtility.MemClear(save.MallocPTR, 64);
}
}
}
For the editor, you would need to copy the JobHandle and void* to a place where it can be called when the editor closes (MonoBehaviour.OnApplicationQuit() or ISystem.OnDestroy() calling a static class that contains the up to date data.)
Yes. From Burst you can call into many Unity APIs that don’t use static objects. And as long as those APIs are also thread-safe for jobs, then you can also call those. Just be careful with Allocator.Persistent, because while it is safe to call from pretty much any thread, it also has a lock inside the internal C++, which can be bad if allocating is frequent within a parallel job.
