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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Marshal.StructureToPtr causes GC alloc calls in Unity

Discussion in 'Scripting' started by Deleted User, Feb 2, 2019.

  1. Deleted User

    Deleted User

    Guest

    Hi everyone,
    I'm trying to use some commonly found code for converting a struct to a byte[] in c#. It uses the System.Runtime.InteropServices.Marshal class. However, it seems in unity that this generates garbage.

    Below is an example script. I am running Unity 2018.3.2f1 with ScriptingRuntimeEnvironment = .NET 4.x equivalent.

    Code (CSharp):
    1. public class MarshallTest : MonoBehaviour
    2. {
    3.     public int testCount = 300;
    4.     byte[] bytes = new byte[1024];
    5.        
    6.     void FixedUpdate()
    7.     {
    8.         for (int i = 0; i < testCount; i++)
    9.         {
    10.             MarshallTestStruct str = new MarshallTestStruct()
    11.             {
    12.                 a = 123456,
    13.                 b = 30.02
    14.             };
    15.  
    16.             GCHandle h = default(GCHandle);
    17.             try
    18.             {
    19.                 h = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    20.  
    21.                 Profiler.BeginSample("Marshal.StructureToPtr");
    22.                 Marshal.StructureToPtr(str, h.AddrOfPinnedObject(), false);
    23.                 Profiler.EndSample();
    24.             }
    25.             finally
    26.             {
    27.                 if (h.IsAllocated) h.Free();
    28.             }
    29.         }
    30.     }
    31. }
    32.  
    33. struct MarshallTestStruct
    34. {
    35.     public long a;
    36.     public double b;
    37. }


    I have tested in development build with the profiler attached and the result is the same. Does anyone know why this is happening? Is there a better way to convert struct to byte[] without any GC alloc?
     
  2. bentontr

    bentontr

    Joined:
    Jun 9, 2017
    Posts:
    39
    Looks like Marshal.StructureToPtr takes an object, so your structure will be boxed and unboxed. Could also be some additional stuff going on under the hood as well. This is just a guess looking at the docs, I didn't actually test it. Maybe try the generic version?
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,385
    There is a generic version available if you target .net framework 4.x:
    https://docs.microsoft.com/en-us/do...ureToPtr__1___0_System_IntPtr_System_Boolean_

    That one shouldn't have boxing garbage.

    Just tested though, and it too creates garbage.

    [edit]
    LOL

    I just looked at the implementation from the .net source:
    https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/marshal.cs,945

    The generic method literally just calls the object version... dafuq.

    Though I guess this makes sense since the object version is an external call and generics won't work for those. But what's the point of even having a generic version then?
     
    Last edited: Feb 3, 2019
  4. meken

    meken

    Joined:
    May 6, 2013
    Posts:
    4
    I don't know if anything changed in the mean time, but I just tried the same: switching to the generic version of Marshal.PtrToStructure and my GC Alloc went straight to zero! So, thanks for that. This is on Unity 2019.3.11f1 on Linux, if that matters at all. And I just tested in the Editor, not even a build.
     
  5. yanfei_game

    yanfei_game

    Joined:
    May 15, 2017
    Posts:
    26
    @meken generic version of Marshal.PtrToStructure work perfectly without calling GC if you try return a struct instance. But generic version of Marshal.StructureToPtr will call GC if you pass a struct instance.
     
  6. KeinZantezuken

    KeinZantezuken

    Joined:
    Mar 28, 2017
    Posts:
    53
    Interesting, so this is what causes massive frame hitching due to GC taking up all the time on recently released Oddworld Soulstorm:
     

    Attached Files:

    PutridEx likes this.
  7. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    1,084
    Wait what???
     
  8. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,653
    Why are you using GCHandle at all here? If you need to pass a pointer to struct to native code, do this via out or ref parameter. Granted, you can't keep that pointer on native side after the call to native returns.