• 学习笔记1:性能问题在于你想不到的任何地方!


    今天在做blqw.Json反序列化的优化工作的时候发现一个问题,在反序列化一个非常简单的对象

    public class SimpleObject
    {
        public static SimpleObject New()
        {
            return new SimpleObject
            {
                Scores = new int[]{1,2,3,4,5,6,7,8,9,0}
            };
        }
        public int[] Scores { get; set; }
    }

    在性能上我居然和想象中的相差非常大,看结果

    我第一时间想到的就是会不会Dictionary引起的?

    delegate bool TryParseHandler(bool nullable, string str, out object value);
    Dictionary<TypeCode, TryParseHandler> TryParseMethods = new Dictionary<TypeCode, TryParseHandler>();
    protected JsonConvert()
    {
        if (Instance == null)
        {
            TryParseMethods = new Dictionary<TypeCode, TryParseHandler>();
            TryParseMethods.Add(Type.GetTypeCode(typeof(Boolean)), TryParseBoolean);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Byte)), TryParseByte);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Char)), TryParseChar);
            TryParseMethods.Add(Type.GetTypeCode(typeof(DateTime)), TryParseDateTime);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Decimal)), TryParseDecimal);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Double)), TryParseDouble);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Int16)), TryParseInt16);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Int32)), TryParseInt32);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Int64)), TryParseInt64);
            TryParseMethods.Add(Type.GetTypeCode(typeof(SByte)), TryParseSByte);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Single)), TryParseSingle);
            TryParseMethods.Add(Type.GetTypeCode(typeof(UInt16)), TryParseUInt16);
            TryParseMethods.Add(Type.GetTypeCode(typeof(UInt32)), TryParseUInt32);
            TryParseMethods.Add(Type.GetTypeCode(typeof(UInt64)), TryParseUInt64);
            TryParseMethods.Add(Type.GetTypeCode(typeof(Guid)), TryParseGuid);
            TryParseMethods.Add(Type.GetTypeCode(typeof(String)), TryParseString);
        }
        else
        {
            TryParseMethods = Instance.TryParseMethods;
        }
    }
    
    public bool TryParse(Type type, string str, out object value)
    {
        bool nullable;
        //处理可空值类型
        Type t;
        t = Nullable.GetUnderlyingType(type);
        if (t != null)
        {
            nullable = true;
            type = t;
        }
        else
        {
            nullable = false;
        }
    
        TryParseHandler tryparse;
        if (TryParseMethods.TryGetValue(Type.GetTypeCode(type), out tryparse))
        {
            return tryparse(nullable, str, out value);
        }
        else
        {
            value = null;
            return false;
        }
    }

    我直接改成了switch,我要查看最坏情况下的性能,所以把int32的case分支放到的最下面

    switch (Type.GetTypeCode(type))
    {
        case TypeCode.Boolean:
            return TryParseBoolean(nullable, str, out value);
        case TypeCode.Byte:
            return TryParseByte(nullable, str, out value);
        case TypeCode.Char:
            return TryParseChar(nullable, str, out value);
        case TypeCode.DateTime:
            return TryParseDateTime(nullable, str, out value);
        case TypeCode.Decimal:
            return TryParseDecimal(nullable, str, out value);
        case TypeCode.Double:
            return TryParseDouble(nullable, str, out value);
        case TypeCode.Int16:
            return TryParseInt16(nullable, str, out value);
        case TypeCode.Int64:
            return TryParseInt64(nullable, str, out value);
        case TypeCode.SByte:
            return TryParseSByte(nullable, str, out value);
        case TypeCode.Single:
            return TryParseSingle(nullable, str, out value);
        case TypeCode.String:
            return TryParseString(nullable, str, out value);
        case TypeCode.UInt16:
            return TryParseUInt16(nullable, str, out value);
        case TypeCode.UInt32:
            return TryParseUInt32(nullable, str, out value);
        case TypeCode.UInt64:
            return TryParseUInt64(nullable, str, out value);
        case TypeCode.DBNull:
        case TypeCode.Empty:
            value = null;
            return str == "null" || str == "undefined" || str == null;
        case TypeCode.Int32:
            return TryParseInt32(nullable, str, out value);
        case TypeCode.Object:
        default:
            value = null;
            return false;
    }

    几乎是没有什么差别的

    所以我果断放弃了Dictionary,我可以把常用的类型(int,string,double等)放到前面,即使最差情况也和Dictionary一样,大部分情况下都会比Dictionary好

    接着我就想会不会是因为我多处理了一个可空值类型,所以性能上会比fastJson慢?(fastJson在反序列化可空值类型的时候是报错的)

    然后我把处理可控制类型的地方给注释了,看看到底是不是判断可空值类型的时候引起的

    bool nullable;
    //处理可空值类型
    //Type t;
    //t = Nullable.GetUnderlyingType(type);
    //if (t != null)
    //{
    //    nullable = true;
    //    type = t;
    //}
    //else
    {
        nullable = false;
    }

    结果几乎没有变化!!!

    嗯,我把TryParseInt32(nullable, str, out value);中处理可控制类型的方法也注释了试试

    public virtual bool TryParseInt32(bool nullable, string str, out object value)
    {
        Int32 v;
        if (Int32.TryParse(str, out v))
        {
            //value = nullable ? new Nullable<Int32>(v) : v;
            value = v;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

    我了个去,性能真的提高了!!

    但是非常奇怪的是,我以前做过测试,判断是几乎不消耗性能的,为什么区区10W次的判断能相差10多毫秒??

    难道问题在三元表达式???

    我试着把三元表达式改成if..else...

    public virtual bool TryParseInt32(bool nullable, string str, out object value)
    {
        Int32 v;
        if (Int32.TryParse(str, out v))
        {
            if (nullable)
            {
                value = new Nullable<Int32>(v);
            }
            else
            {
                value = v;
            }
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

    尼玛坑爹了好吗!!!!

    改成if...else...之后完全和之前的一模一样啊...

    三元表达式这是想表达什么呢?

    ============================================================

    以下感谢博友Pandora的指正

    原本以为是三元表达式的问题,所以也就没有深究下去了

    听了博友Pandora的建议之后回头想想确实也是这么一个道理

    所有方法不可能按照返回值类型做出推断

    为了验证这个说法,只需要设置一个var就可以看出来了

    var 告诉我们 他是可空值类型

    这就说明了 即使是int也会被换成int?

    所以只要改成

    这样就没问题了.....

    好吧好吧  我错怪三元表达式了,三元兄对不起啦~~~~

  • 相关阅读:
    S32K142学习记录_day1
    dsPIC33EP单片机的PPS(外设引脚选择)
    零欧电阻
    MOS管的栅极和源极之间的电阻
    RDLC表格排序设置
    SQL相关
    使用sql的xmlpath可以把xml文件转化为表格
    Visual Studio2017 无法折叠
    使用图形化界面打包自己的类库
    初识NuGet及快速安装使用
  • 原文地址:https://www.cnblogs.com/blqw/p/3300420.html
Copyright © 2020-2023  润新知