Search code examples
c#serializationmarshallingvalue-typeblit

The fastest way to check if a type is blittable?


In my serialiser/deserialiser, I have the following snippet:

    if (element_type.IsValueType && collection_type.IsArray)
    {
        try
        {
            GCHandle h = GCHandle.Alloc(array_object, GCHandleType.Pinned);
            int arrayDataSize = Marshal.SizeOf(element_type) * c.Count;
            var array_data = new byte[arrayDataSize];
            Marshal.Copy(h.AddrOfPinnedObject(), array_data, 0, arrayDataSize);
            h.Free();
            WriteByteArray(array_data);

            return;
        }
        catch (ArgumentException)
        {
            //if the value type is not blittable, then we need to serialise each array item one at a time
        }
    }

The purpose of which is to try and write an array of value types to a stream, in the most efficient way possible (that is, just the content as a bunch of bytes).

The problem comes when the type is a value type but not blittable, and Alloc() fails. At the moment the exception is caught and control passed to code which deals with the array as if it consisted of reference types.

This check however (due to the throwing and catching of the exception which I understand is very slow) is proving to be a severe bottleneck due to the number of value types that are encountered in my application. So I am wondering, what is the fastest way to check if a type is blittable?


Solution

  • I'm using generic class to cache results. Test is done in same way (trying to allocate pinned handle).

    public static class BlittableHelper<T>
    {
        public static readonly bool IsBlittable;
    
        static BlittableHelper()
        {
            try
            {
                // Class test
                if (default(T) != null)
                {
                    // Non-blittable types cannot allocate pinned handle
                    GCHandle.Alloc(default(T), GCHandleType.Pinned).Free();
                    IsBlittable = true;
                }
            }
            catch { }
        }
    }