Search Unity

NativeArray.CopyTo vs Marshal.Copy

Discussion in 'Entity Component System' started by LennartJohansen, Apr 17, 2018.

  1. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Hi.

    I am using the Job system to do some calculations in the editor and at the end I need the data in a managed array. I would think NativeArray.CopyTo was the way to go but this is slow...
    A Marshal.Copy from the NativeArray buffer pointer is 10x+ faster.

    Is there another solution that does not require "unsafe" code?

    Code (csharp):
    1.  
    2.   public static unsafe void NativeToManagedCopyMemory(float[] targetArray, NativeArray<float> SourceNativeArray)
    3. {
    4.             void* memoryPointer = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(sourceNativeArray);
    5.             Marshal.Copy((IntPtr)memoryPointer, targetArray, 0, sourceNativeArray.Length);
    6.  }
    7.  
    Lennart
     
    antoripa likes this.
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Its been optimised for the next 18.1 beta build. So should be as fast or faster with the next build.
     
    antoripa likes this.
  3. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Sounds great :)
     
  4. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,983
    Looking forward to the next build! :)

    Mostly because I am hoping it will have ECS integrated.

    EDIT: out of interest, when should we be using marshall instead? or should we mostly avoid unsafe utility where possible?
     
  5. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    I don't think you should.
     
    antoripa and MadeFromPolygons like this.
  6. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,983
    Okay thanks!

    It was mostly because I was trying to recreate some of the InputSystem github develop branch without access to 2018.2. But 2018.2 is out now so I dont really need to go that deep and directly access memory blocks.
     
  7. chmodseven

    chmodseven

    Joined:
    Jul 20, 2012
    Posts:
    120
    Is there any chance NativeArray.CopyTo might one day be able to work with writing to multidimensional arrays, such as say a float[,] for a Terrain heightmap? Currently the best I can do is:

    Code (CSharp):
    1.  
    2.         float [,] heights = new float [heightMapWidth, heightMapHeight];
    3.         float [] tempHeights = new float [heightMapWidth * heightMapHeight];
    4.         heightMapNativeArray.CopyTo (tempHeights);
    5.         Buffer.BlockCopy (tempHeights, 0, heights, 0, heightMapWidth * heightMapHeight * sizeof (float));
    6.  
    using the tempHeights interim 1D array. But it would be awesome to be able to avoid that extra performance hit if the CopyTo was able to manage the memory side of that behind the scenes.
     
  8. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Hi. try to do something like this. This will add a extention to the NativeArray that copies the data to a multi dimention array in one copy. The target managed array has to be in the correct size. You can add a similar function to NativeSlice.

    Code (csharp):
    1.  
    2.  
    3. public static class NativeArrayExtensions
    4. {
    5.  public static unsafe void CopyToFast<T>(
    6.             this NativeArray<T> nativeArray,
    7.             T[,] array)
    8.             where T : struct
    9.         {
    10.             int byteLength = nativeArray.Length * Marshal.SizeOf(default(T));
    11.             void* managedBuffer = UnsafeUtility.AddressOf(ref array[0,0]);
    12.             void* nativeBuffer = nativeArray.GetUnsafePtr();
    13.             Buffer.MemoryCopy(nativeBuffer, managedBuffer, byteLength, byteLength);
    14.         }
    15. }
    16.  
    17.  
    EDIT:

    after testing some speed I changed the buffer.MemoryCopy to this copy memory function while on windows platform.

    Code (csharp):
    1.  
    2. #if PLATFORM_STANDALONE_WIN
    3.         [DllImport("msvcrt.dll", EntryPoint = "memcpy")]
    4.         public static extern void CopyMemory(IntPtr pDest, IntPtr pSrc, int length);
    5. #endif
    6.  
    Edit2:

    UnsafeUtility.MemCpy seems to be the way to go. No need for the external dll call.

    Code (csharp):
    1.  
    2. UnsafeUtility.MemCpy(managedBuffer, nativeBuffer, byteLength);
    3.  
     
    Last edited: Jun 23, 2018
    Deleted User and SugoiDev like this.
  9. chmodseven

    chmodseven

    Joined:
    Jul 20, 2012
    Posts:
    120
    Thanks, I'll give that a try.

    EDIT: Hey thanks, it works great! Halved the GC in the function, and runtime improved in the profiler by 40%.
     
    Last edited: Jun 24, 2018
  10. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    you should also change the Marshal.SizeOf to UnsafeUtility.SizeOf
    Small GC buildup there also.
     
  11. chmodseven

    chmodseven

    Joined:
    Jul 20, 2012
    Posts:
    120
    Cheers, thanks again.