Newtonsoft.Json.JsonConvert.DeserializeObject首次转换太慢问题

不论是.net还是xamarin工程里面转换json一直使用的都是Newtonsoft包,一直也没觉得有什么问题

直到最近安卓设备出现了很奇怪的事情,有个方法居然花费了1秒多,定位后发现是Newtonsoft.Json.JsonConvert.DeserializeObject花了1秒多,这我就傻眼了,这玩意怎么处理,要是都这样,之前的工程不可能没发现啊。

于是用.net弄了个单元测试,居然也要120多毫秒,这也太慢了吧。

定义类

1
2
3
4
5
6
        public class testjson
        {
            public string aa { get; set; }
            public string bb { get; set; }
            public string cc { get; set; }
        }

测试:

1
2
3
4
5
6
7
            string output = "{ "aa":"1","bb":"哈哈哈哈","cc":"和hi黑欸"} ";
            DateTime start = DateTime.Now;
            testjson res = new testjson();
            object a = Newtonsoft.Json.JsonConvert.DeserializeObject(output, typeof(testjson));
            DateTime end = DateTime.Now;
            TimeSpan bbbb = end - start;
            Console.WriteLine("第一次转换一次花费时间:" + bbbb.TotalMilliseconds);

去网上找了找,看到一些json转换的插件对比测试,Newtonsoft没有这么差啊。做个循环测试,跑个10000次,结果只要26毫秒。

1
2
3
4
5
6
7
8
9
10
            DateTime start1 = DateTime.Now;
            for (int i = 0; i < 10000; i++)
            {
                string output1 = "{ "aa":"1","bb":"哈哈哈哈","cc":"和hi黑欸"} ";
                testjson res1 = new testjson();
                res1 = (testjson)Newtonsoft.Json.JsonConvert.DeserializeObject(output1, typeof(testjson));
            }
            DateTime end1 = DateTime.Now;
            TimeSpan bbbb1 = end1 - start1;
            Console.WriteLine("第二次转换10000次花费时间:" + bbbb1.TotalMilliseconds);

进一步测试,换了个类来转换,同样10000次,只要23毫秒。

换到垃圾安卓板上去测试, 第一次转换1.7秒,接着循环10000次620毫秒。

所以基本可以确定,首次调用 Newtonsoft.Json.JsonConvert.DeserializeObject的时候,会比较慢。

如果没有单独调用第一次,直接循环10000次,就算花费145毫秒,估计也不会发现其中第一次耗费了120秒。

我的想法是,工程启动的时候,随便写个转换先调用下,把第一次的事情做掉,使实际的业务转换受到影响(即使只有第一次)下降到最低。

更多测试,反序列化的和序列化的对比。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
      [TestMethod]
        public void TestMethod1()
        {
            int counts = 10000;
            DateTime t1 = DateTime.Now;
            DateTime t2 = DateTime.Now;

            string output = "{ "aa":"1","bb":"哈哈哈哈","cc":""} ";
            t1 = DateTime.Now;

            testjson res = (testjson)Newtonsoft.Json.JsonConvert.DeserializeObject(output, typeof(testjson));
            t2 = DateTime.Now;

            Console.WriteLine($"第1次反序列化花费时间:{(t2 - t1).TotalMilliseconds}");

            //string output = "{ "flag":"1","cause":"","data":{ "aac004":"1","ake007":"20200226","aac003":"黄方金","fplist":[{"bkc011":"0","bkc010":"0","aae072":"YD027088915","aka063_mc":"诊察费","akc227":"17","aaa027":"350200","aka063":"06","bkc008":"17","bkc009":"0"},{"bkc011":".2","bkc010":"0","aae072":"YD027088915","aka063_mc":"检查费","akc227":"137.2","aaa027":"350200","aka063":"08","bkc008":"111","bkc009":"26"}],"mzlist":[{"bka542_mc":"其中公务员医疗补助","bka542":"bkc059_1","bkeb34":"0.0"}],"akc227":"154.2","bkc014":"20200226","bke174_mc":"省内异地","aac999":"350426198801111010","bkc171":"1002","aaz149":"000000000000","akc190":"YD024926537","bkc075":"0","amc028":"0","bkc059":"0","ake026":"0","aab004":"厦门翼号网络服务有限公司","amc020":"0","aka150":"4","bke174":"1","bkc052":"0","aac002":"350426198801111010","bae029":"1002","bmc041":"0","bkc591":"0","aaz500":"D74781764","bkc102":"0","aae072":"YD027088915","aae011":"lh_hsj","bkc041":"154.2","akc087":"0","bkc040":"0","bkc045":"0","bkc062":"0","ake173":"0","bkc060":"0"},"traceid":""}";
            t1 = DateTime.Now;
            testjson item = new testjson()
            {
                aa = "aljfdlsjflkjsdlf",
                bb = "sdfsdfsdfds啊哈哈的撒发",
                cc = "af;djaf;lkdsjf"
            };



            string mmm = Newtonsoft.Json.JsonConvert.SerializeObject(item);
            t2 = DateTime.Now;
            Console.WriteLine("第1次序列化花费时间:" + (t2 - t1).TotalMilliseconds);

            t1 = DateTime.Now;
            for (int i = 0; i < counts; i++)
            {
                string output1 = "{ "aa":"1","bb":"哈哈哈哈","cc":"和hi黑欸"} ";
                testjson res1 = new testjson();
                res1 = (testjson)Newtonsoft.Json.JsonConvert.DeserializeObject(output1, typeof(testjson));
            }
            t2 = DateTime.Now;

            Console.WriteLine($"第2次反序列化{counts}次花费时间:{(t2 - t1).TotalMilliseconds}");


            t1 = DateTime.Now;
            for (int i = 0; i < counts; i++)
            {
                string nnnnn = Newtonsoft.Json.JsonConvert.SerializeObject(item);
            }
            t2 = DateTime.Now;

            Console.WriteLine($"第2次序列化{counts}次花费时间:{(t2 - t1).TotalMilliseconds}");

            t1 = DateTime.Now;
            for (int i = 0; i < counts; i++)
            {
                if (i % 2 == 1)
                {
                    string output2 = "{ "aaaa":"1","bbb":"哈哈哈哈","ccc":"和hi黑欸"} ";
                    testjson2 res2 = new testjson2();
                    res2 = (testjson2)Newtonsoft.Json.JsonConvert.DeserializeObject(output2, typeof(testjson2));
                }
                else
                {
                    string output1 = "{ "aa":"1","bb":"哈哈哈哈","cc":"和hi黑欸"} ";
                    testjson res1 = new testjson();
                    res1 = (testjson)Newtonsoft.Json.JsonConvert.DeserializeObject(output1, typeof(testjson));
                }
            }
            t2 = DateTime.Now;

            Console.WriteLine($"第2次反序列化{counts}次(反复切换类),花费时间:{(t2 - t1).TotalMilliseconds}");
         

        }


        public class testjson
        {
            public string aa { get; set; }
            public string bb { get; set; }
            public string cc { get; set; }

        }

        public class testjson2
        {
            public string aaa { get; set; }
            public string bbb { get; set; }
            public string ccc { get; set; }

        }
    }
}

测试结果:首次反序列化都要120毫秒,首次序列化也要20多毫秒,反过先做序列化也是首次120毫秒左右,首次反序列化20毫秒左右。再后续一万次的时间也只要20毫秒左右,序列化相对来说比反序列化要快一些。

也就是说 jsonconvert "首次"调用的时候可能在100毫秒左右,然后"首次"调用序列化或者反序列化需要20毫秒左右,再往后的调用耗时就几乎可以忽略不计了。

目前我的猜想是首次调用做了一些初始化的工作,具体的后续去看下newtonsoftd的源码再分析,待续....