.net BinaryFormatter序列化对象慢的原因

    最近在做组件对象写入流的优化,因此对一些.net下序列化组件做了一些测试,分别针对ProtoBuf.net 和.net自带的BinaryFormatter进行了分析.从测试的结果来看BinaryFormatter的性能和ProtoBuf.net的性能足足相差了10倍。为什么差这么远呢,如果紧紧从运行时间来看可能以为BinaryFormatter一定是使用反射什么的,所以导致结果这么慢。为了更清楚的了解具体情况于是对两者的测试代码进行了一个内存分析.

ProtoBuf.net的测试代码

        public void PB(int count)
        {
            TestPB obj = new TestPB();
            obj.Email = "henryfan@test.com";
            obj.FirstName = "henry";
            obj.LastName = "fan";
            obj.ID = 3456;
            obj.Phone = "13418888121";
            obj.Type = 67;
            System.IO.MemoryStream stream = new System.IO.MemoryStream(265);
            stream.Position = 0;
            ProtoBuf.Serializer.Serialize<TestPB>(stream, obj);
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
            {
                stream.Position = 0;
                ProtoBuf.Serializer.Serialize<TestPB>(stream, obj);
            }
            sw.Stop();
            Console.WriteLine("ProtoBuf Serialize Objects{0} Time:{1}", count, sw.Elapsed.TotalMilliseconds);
        }

BinaryFormatter的测试代码

        public void DotNet(int count)
        {

            TestDotNet obj = new TestDotNet();
            obj.Email = "henryfan@test.com";
            obj.FirstName = "henry";
            obj.LastName = "fan";
            obj.ID = 3456;
            obj.Phone = "13418888121";
            obj.Type = 67;
            System.IO.MemoryStream stream = new System.IO.MemoryStream(256);
            BinaryFormatter bf = new BinaryFormatter();
            stream.Position = 0;
            bf.Serialize(stream, obj);
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
            {
                stream.Position = 0;
                bf.Serialize(stream, obj);
            }
            sw.Stop();
            Console.WriteLine("BinaryFormatter Serialize Objects{0} Time:{1}", count,sw.Elapsed.TotalMilliseconds);
        }

通过内存分析针对上面代码进行100000次的序列化对象查看其内存开销的情况

ProtoBuf.net 的内存使用情况

名称 非独占分配数 独占分配数 非独占字节数 独占字节数 非独占分配数百分比
+ ProtoBuf.ProtoWriter 100,001 100,001 6,000,060 6,000,060 24.44
+ ProtoBuf.NetObjectCache 100,001 100,001 2,400,024 2,400,024 24.44
+ ProtoBuf.Meta.RuntimeTypeModel.TypeFinder 200,002 200,002 2,400,024 2,400,024 48.88
+ System.Byte[] 1,199 1,199 2,111,060 2,111,060 0.29
+ System.String 697 697 32,418 32,418 0.17
+ System.Char[] 44 44 25,382 25,382 0.01
+ System.Object[] 350 350 20,300 20,300 0.09
+ System.Collections.Generic.List`1 589 589 14,136 14,136 0.14
+ System.Reflection.Emit.OpCode 226 226 9,944 9,944 0.06
+ System.Reflection.MethodInfo[] 256 256 9,012 9,012 0.06
+ System.Reflection.RuntimeMethodInfo 123 123 6,888 6,888 0.03
+ System.Collections.ArrayList 259 259 6,216 6,216 0.06

BinaryFormatter的内存使用情况

名称 非独占分配数 独占分配数 非独占字节数 独占字节数 非独占分配数百分比
+ System.Collections.Hashtable.bucket[] 500,008 500,008 72,001,656 72,001,656 8.77
+ System.Object[] 600,012 600,012 39,204,636 39,204,636 10.52
+ System.Byte[] 201,074 201,074 31,699,205 31,699,205 3.53
+ System.Collections.Hashtable 500,008 500,008 28,000,448 28,000,448 8.77
+ System.Runtime.Serialization.Formatters.Binary.NameInfo 400,004 400,004 19,200,192 19,200,192 7.01
+ System.String 100,078 100,078 19,005,036 19,005,036 1.75
+ System.Int64[] 100,001 100,001 17,200,172 17,200,172 1.75
+ System.Runtime.Serialization.Formatters.Binary.ObjectWriter 100,001 100,001 10,400,104 10,400,104 1.75
+ System.Runtime.Serialization.Formatters.Binary.__BinaryWriter 100,001 100,001 10,000,100 10,000,100 1.75
+ System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo 100,001 100,001 6,800,068 6,800,068 1.75
+ System.Runtime.Serialization.Formatters.Binary.SerStack 200,002 200,002 4,800,048 4,800,048 3.51
+ System.Runtime.Serialization.Formatters.Binary.BinaryObjectWithMapTyped 100,001 100,001 4,400,044 4,400,044 1.75

从上面两个内存分析结果来看就一目了然了,ProtoBuf.net在序列化的过程中紧紧只开销了13MB左右的内存,所创建对象的总数大概在400000个左右;反观BinaryFormatter在序列化过程确使用了300多MB的内存,创建对象总数接近6000000个。紧紧是对象的创建对象的数量就已经是ProtoBuf.net 10倍,因此效率慢就是正常的事。MS为什么这样做这个就不得而知了,明明可以做得很好的,但确并没这样做……

访问Beetlex的Github
原文地址:https://www.cnblogs.com/smark/p/2485656.html