Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance Byte[] to Generic

I'm in need to make a byte[] -> T extension method and need it to be fast (no need for it being pretty)

This function will be called 100's of 1000's of times in very short succession in an absolute performance critical environment.

We're currently optimizing on "ticks" level, every tick translates to a couple milliseconds higher in the callstack, thus the need of raw speed over maintainability (not how I like to design software, but the reasoning behind this is out of scope).

Consider the following code, it's clean and maintainable, but it's relatively slow (probably due to boxing and unboxing), Can this be optimized to be faster?

public static T ConvertTo<T>(this byte[] bytes, int offset = 0)
{
    var type = typeof(T);
    if (type == typeof(sbyte)) return bytes[offset].As<T>();
    if (type == typeof(byte)) return bytes[offset].As<T>();
    if (type == typeof(short)) return BitConverter.ToInt16(bytes, offset).As<T>();
    if (type == typeof(ushort)) return BitConverter.ToUInt32(bytes, offset).As<T>();
    if (type == typeof(int)) return BitConverter.ToInt32(bytes, offset).As<T>();
    if (type == typeof(uint)) return BitConverter.ToUInt32(bytes, offset).As<T>();
    if (type == typeof(long)) return BitConverter.ToInt64(bytes, offset).As<T>();
    if (type == typeof(ulong)) return BitConverter.ToUInt64(bytes, offset).As<T>();

    throw new NotImplementedException();
}

public static T As<T>(this object o)
{
    return (T)o;
}
like image 639
Riaan Walters Avatar asked Mar 22 '26 07:03

Riaan Walters


1 Answers

I also needed this and I have found that conversion to these primitive types can be done faster using pointers and unsafe code. Like:

public unsafe int ToInt(byte[] bytes, int offset)
{
    fixed(byte* ptr = bytes)
    {
         return *(int*)(ptr + offset);
    }
}

But C# unfortunately does not support generic pointer types, so I had to write this peace of code in IL whitch doesn't care much about generic constraints:

.method public hidebysig static !!T  Read<T>(void* ptr) cil managed
{
    .maxstack  8
    nop         
    ldarg.0     
    ldobj !!T
    ret 
}

It's not pretty but it seems to work in my case. Be careful when using this method - you can pass any type as argument and I don't know what it will do.

You can find the entire il file on github or download compiled assembly https://github.com/exyi/RaptorDB-Document/blob/master/GenericPointerHelpers/GenericPointerHelpers.dll?raw=true I have few more helpers there.

EDIT: I have forgotten to write how to use this method. Assuming you have compiled the method in GenericPointerHelper class like me you can implement your ConvertTo method bit similar to my ToInt:

public unsafe T ConvertTo<T>(byte[] bytes, int offset)
    where T: struct // not needed to work, just to eliminate some errors
{
    fixed(byte* ptr = bytes)
    {
         return GenericPointerHelper.Read<T>(ptr + offset);
    }
}
like image 157
exyi Avatar answered Mar 23 '26 22:03

exyi