HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Fastest De-/Serialise struct in .Net

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
netstructfastestserialise

Problem

I wrote this code to De- and serialise structures afap using IL generation (comments describes C# analogue). What can be improved here?

```
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

namespace StructInterop
{
public static class StructInterOp
{
static readonly ConstructorInfo IntPtrCtor = typeof(IntPtr).GetConstructor(new[] { typeof(void*) });
static readonly MethodInfo MarshalCopy = typeof(Marshal).GetMethod("Copy", new[] { typeof(IntPtr), typeof(byte[]), typeof(int), typeof(int) });
private static class DelegateHolder where T : struct
{
// ReSharper disable MemberHidesStaticFromOuterClass
// ReSharper disable StaticMemberInGenericType
public static readonly Type TypeOfT = typeof(T);
public static readonly int SizeInBytes = Marshal.SizeOf(TypeOfT);

public static readonly Func Serialize = CreateSerializationDelegate();
public static readonly Func Deserialize = CreateDeserializationDelegate();

//public static byte[] Serialize(T value)
//{
// IntPtr p = new IntPtr(&value);
// byte[] result = new byte[sizeof(T)];
// Marshal.Copy(p, result, 0, result.Length);
// return result;
//}
private static Func CreateSerializationDelegate()
{
var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
typeof(byte[]),
new[] { TypeOfT },
Assembly.GetExecutingAssembly().ManifestModule);
dm.DefineParameter(1, ParameterAttributes.None, "value");

var generator = dm.GetILGenerator();
generator.DeclareLocal(typeof(byte[]));

//IntPtr p = new IntPtr(&value);
generator.Emit(OpCodes.Ldarga_S, (byte)0);
generator.Emit(OpCodes.Conv_U)

Solution

Naming

Interop should be treated as a single word like in System.Runtime.InteropServices. You should also not name a class the same as its namespace. Which is why this is really not good:

namespace StructInterop
{
    public static class StructInterOp
    {


Given that the StructInterOp class has methods like Serialize and Deserialize - why not call it StructSerializer?
Structure

You could remove a lot of your comments by spliting up the generation a bit. (The names for the new methods aren't great...)

//public static byte[] Serialize(T value)
//{
//    IntPtr p = new IntPtr(&value);
//    byte[] result = new byte[sizeof(T)];
//    Marshal.Copy(p, result, 0, result.Length);
//    return result;
//}
private static Func CreateSerializationDelegate()
{
    var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
        typeof(byte[]),
        new[] { TypeOfT },
        Assembly.GetExecutingAssembly().ManifestModule);
    dm.DefineParameter(1, ParameterAttributes.None, "value");

    var generator = dm.GetILGenerator();
    generator.DeclareLocal(typeof(byte[]));

    GenerateIntPtr(generator);
    GenerateResultArray(generator);
    GenerateCopy(generator);
    GenerateReturn(generator);

    return (Func)dm.CreateDelegate(typeof(Func));
}

private static void GenerateReturn(ILGenerator generator)
{
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ret);
}

private static void GenerateCopy(ILGenerator generator)
{
    generator.Emit(OpCodes.Stloc_0);
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ldc_I4_0);
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ldlen);
    generator.Emit(OpCodes.Conv_I4);
    generator.EmitCall(OpCodes.Call, MarshalCopy, null);
}

private static void GenerateResultArray(ILGenerator generator)
{
    OpCode ldcStructSize = SizeInBytes < sbyte.MaxValue ? OpCodes.Ldc_I4_S : OpCodes.Ldc_I4;
    generator.Emit(ldcStructSize, SizeInBytes);
    generator.Emit(OpCodes.Newarr, typeof(byte));
}

private static void GenerateIntPtr(ILGenerator generator)
{
    generator.Emit(OpCodes.Ldarga_S, (byte)0);
    generator.Emit(OpCodes.Conv_U);
    generator.Emit(OpCodes.Newobj, IntPtrCtor);
}


Comments

/// 
/// Do not check array bounds, possible buffer overflow
/// 
public static T Deserialize(byte[] data) where T : struct


That's not a good comment. It should summarise what the method does:

/// 
/// Deserializes the given data to the specified type.
/// 
/// The serialized struct data.
/// The type of the struct to be deserialized.
/// An instance of type 

Code Snippets

namespace StructInterop
{
    public static class StructInterOp
    {
//public static byte[] Serialize(T value)
//{
//    IntPtr p = new IntPtr(&value);
//    byte[] result = new byte[sizeof(T)];
//    Marshal.Copy(p, result, 0, result.Length);
//    return result;
//}
private static Func<T, byte[]> CreateSerializationDelegate()
{
    var dm = new DynamicMethod("Serialize" + TypeOfT.Name,
        typeof(byte[]),
        new[] { TypeOfT },
        Assembly.GetExecutingAssembly().ManifestModule);
    dm.DefineParameter(1, ParameterAttributes.None, "value");

    var generator = dm.GetILGenerator();
    generator.DeclareLocal(typeof(byte[]));

    GenerateIntPtr(generator);
    GenerateResultArray(generator);
    GenerateCopy(generator);
    GenerateReturn(generator);

    return (Func<T, byte[]>)dm.CreateDelegate(typeof(Func<T, byte[]>));
}

private static void GenerateReturn(ILGenerator generator)
{
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ret);
}

private static void GenerateCopy(ILGenerator generator)
{
    generator.Emit(OpCodes.Stloc_0);
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ldc_I4_0);
    generator.Emit(OpCodes.Ldloc_0);
    generator.Emit(OpCodes.Ldlen);
    generator.Emit(OpCodes.Conv_I4);
    generator.EmitCall(OpCodes.Call, MarshalCopy, null);
}

private static void GenerateResultArray(ILGenerator generator)
{
    OpCode ldcStructSize = SizeInBytes < sbyte.MaxValue ? OpCodes.Ldc_I4_S : OpCodes.Ldc_I4;
    generator.Emit(ldcStructSize, SizeInBytes);
    generator.Emit(OpCodes.Newarr, typeof(byte));
}

private static void GenerateIntPtr(ILGenerator generator)
{
    generator.Emit(OpCodes.Ldarga_S, (byte)0);
    generator.Emit(OpCodes.Conv_U);
    generator.Emit(OpCodes.Newobj, IntPtrCtor);
}
/// <summary>
/// Do not check array bounds, possible buffer overflow
/// </summary>
public static T Deserialize<T>(byte[] data) where T : struct
/// <summary>
/// Deserializes the given data to the specified type.
/// </summary>
/// <paramref name="data">The serialized struct data.</paramref>
/// <typeparam name="T">The type of the struct to be deserialized.</typeparam>
/// <returns>An instance of type <typeparamref name="T"/></returns>

Context

StackExchange Code Review Q#97120, answer score: 7

Revisions (0)

No revisions yet.