Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 779
sebf Avatar asked May 13 '12 19:05

sebf


1 Answers

The current answer works for the questioner's case but, according to the specification, arrays of blittable value types are also blittable types themselves. Extended Ondřej's method a bit, so it takes this into account, and also works for reference types:

public static bool IsBlittable<T>()
{
    return IsBlittableCache<T>.Value;
}

public static bool IsBlittable(Type type)
{
    if(type.IsArray)
    {
        var elem = type.GetElementType();
        return elem.IsValueType && IsBlittable(elem);
    }
    try{
        object instance = FormatterServices.GetUninitializedObject(type);
        GCHandle.Alloc(instance, GCHandleType.Pinned).Free();
        return true;
    }catch{
        return false;
    }
}

private static class IsBlittableCache<T>
{
    public static readonly bool Value = IsBlittable(typeof(T));
}

As a side effect, this returns (albeit correctly) false for string, because GetUninitializedObject can't create it. Assuming Alloc really checks for blittability (except for string), this should be reliable.

like image 85
IS4 Avatar answered Sep 26 '22 21:09

IS4