Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question What is the recommended way to copy large arrays as input into job structs?

Discussion in 'Entity Component System' started by jakesee, Aug 29, 2023.

  1. jakesee

    jakesee

    Joined:
    Jan 7, 2020
    Posts:
    24
    Code (CSharp):
    1.  
    2. [BurstCompile]
    3. struct MyJob : IJob {
    4.  
    5.    [ReadOnly] NativeArray<float3> datapoints; // this is a large array
    6.    [WriteOnly] NativeArray<float3> results; // this is also large array
    7.  
    8.    public void Execute() {
    9.       // blah
    10.    }
    11. }
    Give the above job with large arrays, what is the most efficient way to copy the data to/from C# managed collections within the main thread?

    If datapoints is the same for every Execute(), how can I avoid copying the input data so that all jobs to reference the same set of datapoints?

    Thanks.
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    Most efficient is to not copy them at all. Use [same] native collections for both with Allocator.Persistent.

    Just in case, native collections aren't deep copied when used like:
    Code (CSharp):
    1. NativeArray<float3> someArr;
    2. NativeArray<float3> refCopy = someArr;
    Only pointers inside struct are copied. Native containers are value types that act like a reference type. So there's no major overhead when copying "reference" to the container.
     
    mikaelK likes this.
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,556
    Mark data as dirty, when data changes. And process data only then.

    You could even collect, which index has changed, and update only changed indexes.

    On side note, you can iterate in parallel for job, to process arrays.

    As long collections are unmanaged, you should be fine using burst. Which adds to the process speed.

    There are also there ways, but unable to give of top of my head. Others will add comments perhaps.
     
  4. jakesee

    jakesee

    Joined:
    Jan 7, 2020
    Posts:
    24
    Thanks @xVergilx will go for Allocator.Persistent for the shared data.

    I am writing new jobs to be used with some existing libraries. For example, on the main thread the data structure is float[,] but the result from the job is NativeArray<float> I don't know any way to copy the data between the data structures for than a brute for nested for-loop. Any advice?

    @Antypodish the shared datapoints is the same but the result will be different based on other input params (not shown in example). Good thing is datapoints will be readonly.
     
  5. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    Copying two-dimensional managed array to native array is a bit more tricky.
    NativeArray implementation of CopyFrom looks like this:
    - CopyFrom ->
    - Length equality check first then;
    - CopySafe call:
    Code (CSharp):
    1.       private static unsafe void CopySafe(T[] src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) {
    2.          AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
    3.          NativeArray<T>.CheckCopyPtr(src);
    4.          NativeArray<T>.CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length);
    5.          GCHandle gcHandle = GCHandle.Alloc((object) src, GCHandleType.Pinned);
    6.          IntPtr num = gcHandle.AddrOfPinnedObject();
    7.          UnsafeUtility.MemCpy((void*) ((IntPtr) dst.m_Buffer + dstIndex * UnsafeUtility.SizeOf<T>()),
    8.                               (void*) ((IntPtr) (void*) num + srcIndex * UnsafeUtility.SizeOf<T>()),
    9.                               (long) (length * UnsafeUtility.SizeOf<T>()));
    10.          gcHandle.Free();
    11.       }
    Basically its a MemCpy.

    Multidimensional arrays should be layout out in the same data block row-major order.
    https://stackoverflow.com/questions/73368641/how-are-multi-dimensional-arrays-stored-net

    So, make a NativeArray<float2>.
    Add a test for the two arrays values first with brute-force copy to have something to compare MemCpy based copy against. Then figure out the length of required data, do a copy and in theory it should boil down to something like:
    Code (CSharp):
    1. // where T == float2 or unmanaged
    2. // Optionally add length comparison check / throw first
    3.          int byteLength = managedArrayLength * UnsafeUtility.SizeOf<T>();
    4.          void* managedBuffer = UnsafeUtility.AddressOf(ref array[0,0]);
    5.          void* nativeBuffer = nativeArray.GetUnsafePtr();
    6.  
    7.          UnsafeUtility.MemCpy(managedBuffer, nativeBuffer, byteLength);
    If you want to copy back from NativeArray to managed array - swap src / dst params for the MemCpy.
     
    Last edited: Aug 29, 2023
  6. apkdev

    apkdev

    Joined:
    Dec 12, 2015
    Posts:
    263
    jakesee and xVergilx like this.