Skip to content

Instantly share code, notes, and snippets.

@kornman00
Created April 14, 2016 21:14
Show Gist options
  • Save kornman00/86b1701f228c0611501dd0cdb7d24d69 to your computer and use it in GitHub Desktop.
Save kornman00/86b1701f228c0611501dd0cdb7d24d69 to your computer and use it in GitHub Desktop.
Avoiding heap allocations in generic serialization code
// #NOTE: be mindful of how large T actually is and whether or not your runtime does or doesn't
// optimize passing large structs by reference instead of copying bits to other parts of the stack
public static T Read<T>(this BinaryReader reader, T item = default(T))
where T : IStreamSerializable, new()
{
if (item == null) // value types are never null, so this helps us avoid heap allocations
item = new T();
item.Load(reader);
return item;
}
.method public hidebysig static
!!T Read<.ctor (IStreamSerializable) T> (
class [mscorlib]System.IO.BinaryReader reader,
[opt] !!T item
) cil managed
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x1d9f0
// Code size 64 (0x40)
.maxstack 5
.locals init (
[0] !!T
)
IL_0000: ldarg.1
IL_0001: box !!T
IL_0006: brtrue IL_0030
IL_000b: ldloca.s 0
IL_000d: initobj !!T
IL_0013: ldloc.0
IL_0014: box !!T
IL_0019: brfalse IL_0029
IL_001e: ldloca.s 0
IL_0020: initobj !!T
IL_0026: ldloc.0
IL_0027: br.s IL_002e
IL_0029: call !!0 [mscorlib]System.Activator::CreateInstance<!!T>()
IL_002e: starg.s item
IL_0030: ldarga.s item
IL_0032: ldarg.0
IL_0033: constrained. !!T
IL_0039: callvirt instance void IStreamSerializable::Load(class [mscorlib]System.IO.BinaryReader)
IL_003e: ldarg.1
IL_003f: ret
} // end of method SerializationExtensions::Read
public static T Read<T>(this BinaryReader reader, T item = null) where T : IStreamSerializable, new()
{
if (item == null)
{
item = ((default(T) == null) ? Activator.CreateInstance<T>() : default(T));
}
item.Load(reader);
return item;
}
public static T Read<T>(this BinaryReader reader)
where T : IStreamSerializable, new()
{
T item = new T(); // when T is a value type, this causes a heap allocation in Unity!
item.Load(reader);
return item;
}
**** Type: MY_STRUCT // names have been changed to protect the innocent
Unique backtraces 22
Avg Alloc 238.500000
Avg Free 2.000000
Avg Garbage 0.838574
Frame Count 2
Allocs 477
Frees 4
Skipped Alloc 0
Skipped Free 0
91 (%19.077568) // number of allocations from this specific backtrace
Sample address 000000002620D0F0
(wrapper managed-to-native) object:__icall_wrapper_mono_object_new_specific
(wrapper managed-to-native) object:__icall_wrapper_mono_object_new_specific
SerializationExtensions:Read<MY_STRUCT> C:\[REDACTED]\Serialization.cs(330)
[REDACTED...]
(wrapper runtime-invoke) object:runtime_invoke_void__this__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment