检查的最快方式,如果一个类型是blittable?(The fastest way to check

2019-09-17 05:17发布

在我的串行器/解串器,我有下面的代码片段:

    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
        }
    }

其目的是,试图写值类型的数组到流,在可能的最有效的方式(即,只是将内容作为一串字节)。

问题是当该类型是值类型,但不blittable,或Alloc()失败。 目前,异常被捕获并控制传递给代码,就好像它包括引用类型与数组交易。

然而,这种检查(由于投掷和我的理解是很慢的异常捕获)被证明是一个严重的瓶颈问题,因为到在我的应用中遇到的值类型的数量。 所以,我想知道, 什么是检查一个类型是blittable最快的方法是什么?

Answer 1:

我使用泛型类缓存结果。 测试是在同样的方式进行(试图分配固定手柄)。

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 { }
    }
}


Answer 2:

目前的答案工程为提问者的情况下,但是,根据规范,blittable值类型的数组也Blittable型本身。 扩展的Ondrej的方法一点,所以考虑到了这,也适用于引用类型:

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));
}

作为一个副作用,这将返回(尽管是正确的) falsestring ,因为GetUninitializedObject不能创建它。 假设Alloc真的检查blittability(除string ),这应该是可靠的。



Answer 3:

优异的代码由@ IllidanS4此页面上错误地返回false ,其中所述元件是blittable数组格式化类型的,这意味着该阵列是blittable也。 从这个例子开始,我固定的问题,并添加处理了几个把握不好的情况下,如:

  • T[]其中T :格式化型(刚才提到)
  • 交错数组int[][][]...
  • 枚举(但不是System.Enum ittself)
  • 接口,抽象类型
  • 泛型类型(从不blittable)。

我还添加所作的情况下,避免了昂贵的Exception挡了一下更详尽跑单元测试的所有不同种类的,我能想到的类型。

public static bool IsBlittable(this Type T)
{
    while (T.IsArray)
        T = T.GetElementType();

    bool b;
    if (!((b = T.IsPrimitive || T.IsEnum) || T.IsAbstract || T.IsAutoLayout || T.IsGenericType))
        try
        {
            GCHandle.Alloc(FormatterServices.GetUninitializedObject(T), GCHandleType.Pinned).Free();
            b = true;
        }
        catch { }
    return b;
}

应使用从对方的回答漂亮的缓存机制原样。



Answer 4:

使用http://msdn.microsoft.com/en-us/library/system.type.islayoutsequential.aspx和http://msdn.microsoft.com/en-us/library/system.type.isexplicitlayout.aspx :

element_type.IsValueType && collection_type.IsArray && (element_type.IsLayoutSequential || element_type.IsExplicitLayout)


Answer 5:

最快的方式是不分配,但重复使用现有的GCHandle这样的:

var gch = GCHandle.Alloc(null, GCHandleType.Pinned);
gch.Target = new byte[0];
gch.Target = "";

GCHandle.Alloc分配或与服用锁这是相对昂贵的操作,每次重复使用现有的槽。 和静态只读原始类型jitting时,但在泛型类型不商店的GCHandle因为每个通用instantination将自身拷贝变成常数。



文章来源: The fastest way to check if a type is blittable?