您现在的位置是:首页 > 文章详情

C#高性能二进制序列化

日期:2018-06-22点击:472

        二进制序列化可以方便快捷的将对象进行持久化或者网络传输,并且体积小、性能高,应用面甚至还要高于json的序列化;开始之前,先来看看dotcore/dotne自带的二进制序列化:C#中对象序列化和反序列化一般是通过BinaryFormatter类来实现的二进制序列化、反序列化的。

        BinaryFormatter序列化:

1 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 2 3 System.IO.MemoryStream memStream = new System.IO.MemoryStream(); 4 5 serializer.Serialize(memStream, request);

        BinaryFormatter反序列化:

 1 memStream.Position=0;  2  3 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer =  4  5 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();  6  7 object newobj = deserializer.Deserialize(memStream);  8  9  memStream.Close(); 10 11 return newobj;

        用着多了就发现BinaryFormatter有很多地方不妥,下面就来数数这个序列化的“三宗罪”:

        1.类名上面要加上[Serializable],不加不给序列化;正常的用法应该是序列化一个对象,不需的地方加上NonSerialized才合理吧;

        2.序列化byte[]结果非常大,使用System.Text.Encoding.UTF8.GetString(bytes)查看下,发现里面有一大堆的元数据;对比看看google的protobuf,pb为什么在网络上应用的越来越多,这和他本身序列化完后体积小有着绝大部门的原因;

        3.序列化对象需要完全一致,连类的命名空间都要相同,这点对于分面式开发的应用来说也是不可接受的;

        既然BinaryFormatter不好用,那就只能动手自行实现一个解决上述问题的二进制序列化方案;首先去掉[Serializable]这个标签,接着主要是分析对象,并定义对象序列化后的数据结构;这里的想法是按长度加内容的方式来定义,举个例子:使用int作为长度,来保存一个int值,序列化完应该是:4,0,0,0,1,0,0,0这样的一组bytes,同理可以将int、short、long、float、double、datetime、enum、array、string、class、generic等按照这个格式进行序列化,这里主要使用的是BitConverter、反射等来实现序列化与反序列化;

        序列化实现如下:

 1 public static byte[] Serialize(object param)  2  {  3 List<byte> datas = new List<byte>();  4  5 var len = 0;  6  7 byte[] data = null;  8  9 if (param == null)  10  {  11 len = 0;  12  }  13 else  14  {  15 if (param is string)  16  {  17 data = Encoding.UTF8.GetBytes((string)param);  18  }  19 else if (param is byte)  20  {  21 data = new byte[] { (byte)param };  22  }  23 else if (param is bool)  24  {  25 data = BitConverter.GetBytes((bool)param);  26  }  27 else if (param is short)  28  {  29 data = BitConverter.GetBytes((short)param);  30  }  31 else if (param is int)  32  {  33 data = BitConverter.GetBytes((int)param);  34  }  35 else if (param is long)  36  {  37 data = BitConverter.GetBytes((long)param);  38  }  39 else if (param is float)  40  {  41 data = BitConverter.GetBytes((float)param);  42  }  43 else if (param is double)  44  {  45 data = BitConverter.GetBytes((double)param);  46  }  47 else if (param is DateTime)  48  {  49 var str = "wl" + ((DateTime)param).Ticks;  50 data = Encoding.UTF8.GetBytes(str);  51  }  52 else if (param is Enum)  53  {  54 var enumValType = Enum.GetUnderlyingType(param.GetType());  55  56 if (enumValType == typeof(byte))  57  {  58 data = new byte[] { (byte)param };  59  }  60 else if (enumValType == typeof(short))  61  {  62 data = BitConverter.GetBytes((Int16)param);  63  }  64 else if (enumValType == typeof(int))  65  {  66 data = BitConverter.GetBytes((Int32)param);  67  }  68 else  69  {  70 data = BitConverter.GetBytes((Int64)param);  71  }  72  }  73 else if (param is byte[])  74  {  75 data = (byte[])param;  76  }  77 else  78  {  79 var type = param.GetType();  80  81  82 if (type.IsGenericType || type.IsArray)  83  {  84 if (TypeHelper.DicTypeStrs.Contains(type.Name))  85 data = SerializeDic((System.Collections.IDictionary)param);  86 else if (TypeHelper.ListTypeStrs.Contains(type.Name) || type.IsArray)  87 data = SerializeList((System.Collections.IEnumerable)param);  88 else  89 data = SerializeClass(param, type);  90  }  91 else if (type.IsClass)  92  {  93 data = SerializeClass(param, type);  94  }  95  96  }  97 if (data != null)  98 len = data.Length;  99  } 100  datas.AddRange(BitConverter.GetBytes(len)); 101 if (len > 0) 102  { 103  datas.AddRange(data); 104  } 105 return datas.Count == 0 ? null : datas.ToArray(); 106 }
View Code

        反序列化实现如下:

 1 public static object Deserialize(Type type, byte[] datas, ref int offset)  2  {  3 dynamic obj = null;  4  5 var len = 0;  6  7 byte[] data = null;  8  9 len = BitConverter.ToInt32(datas, offset);  10 offset += 4;  11 if (len > 0)  12  {  13 data = new byte[len];  14 Buffer.BlockCopy(datas, offset, data, 0, len);  15 offset += len;  16  17 if (type == typeof(string))  18  {  19 obj = Encoding.UTF8.GetString(data);  20  }  21 else if (type == typeof(byte))  22  {  23 obj = (data);  24  }  25 else if (type == typeof(bool))  26  {  27 obj = (BitConverter.ToBoolean(data, 0));  28  }  29 else if (type == typeof(short))  30  {  31 obj = (BitConverter.ToInt16(data, 0));  32  }  33 else if (type == typeof(int))  34  {  35 obj = (BitConverter.ToInt32(data, 0));  36  }  37 else if (type == typeof(long))  38  {  39 obj = (BitConverter.ToInt64(data, 0));  40  }  41 else if (type == typeof(float))  42  {  43 obj = (BitConverter.ToSingle(data, 0));  44  }  45 else if (type == typeof(double))  46  {  47 obj = (BitConverter.ToDouble(data, 0));  48  }  49 else if (type == typeof(decimal))  50  {  51 obj = (BitConverter.ToDouble(data, 0));  52  }  53 else if (type == typeof(DateTime))  54  {  55 var dstr = Encoding.UTF8.GetString(data);  56 var ticks = long.Parse(dstr.Substring(2));  57 obj = (new DateTime(ticks));  58  }  59 else if (type.BaseType == typeof(Enum))  60  {  61 var numType = Enum.GetUnderlyingType(type);  62  63 if (numType == typeof(byte))  64  {  65 obj = Enum.ToObject(type, data[0]);  66  }  67 else if (numType == typeof(short))  68  {  69 obj = Enum.ToObject(type, BitConverter.ToInt16(data, 0));  70  }  71 else if (numType == typeof(int))  72  {  73 obj = Enum.ToObject(type, BitConverter.ToInt32(data, 0));  74  }  75 else  76  {  77 obj = Enum.ToObject(type, BitConverter.ToInt64(data, 0));  78  }  79  }  80 else if (type == typeof(byte[]))  81  {  82 obj = (byte[])data;  83  }  84 else if (type.IsGenericType)  85  {  86 if (TypeHelper.ListTypeStrs.Contains(type.Name))  87  {  88 obj = DeserializeList(type, data);  89  }  90 else if (TypeHelper.DicTypeStrs.Contains(type.Name))  91  {  92 obj = DeserializeDic(type, data);  93  }  94 else  95  {  96 obj = DeserializeClass(type, data);  97  }  98  }  99 else if (type.IsClass) 100  { 101 obj = DeserializeClass(type, data); 102  } 103 else if (type.IsArray) 104  { 105 obj = DeserializeArray(type, data); 106  } 107 else 108  { 109 throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定义的类型:" + type.ToString()); 110  } 111 112  } 113 return obj; 114 }
View Code

        其他详细的代码可以查看ParamsSerializeUtil.cs

        功能基本实现了,下面对比一下10000次的实体序列化与反序列化测试结果:

        实体代码:

 1 var groupInfo = new GroupInfo()  2  {  3 GroupID = 1,  4 IsTemporary = false,  5 Name = "yswenli group",  6 Created = DateTimeHelper.Now,  7 Creator = new UserInfo()  8  {  9 10 ID = 1, 11 Birthday = DateTimeHelper.Now.AddYears(-100), 12 UserName = "yswenli" 13  }, 14 Users = new System.Collections.Generic.List<UserInfo>() 15  { 16 new UserInfo() 17  { 18 19 ID = 1, 20 Birthday = DateTimeHelper.Now.AddYears(-100), 21 UserName = "yswenli" 22  } 23  } 24 };

        测试代码:

 1 public static byte[] SerializeBinary(object request)  2  {  3  4 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =  5  6 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();  7  8 using (System.IO.MemoryStream memStream = new System.IO.MemoryStream())  9  { 10  serializer.Serialize(memStream, request); 11 12 return memStream.ToArray(); 13  } 14  } 15 16 17 public static object DeSerializeBinary(byte[] data) 18  { 19 using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(data)) 20  { 21 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = 22 23 new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 24 25 return deserializer.Deserialize(memStream); 26  } 27  } 28 29 static void SerializeTest() 30  { 31 var groupInfo = new GroupInfo() 32  { 33 GroupID = 1, 34 IsTemporary = false, 35 Name = "yswenli group", 36 Created = DateTimeHelper.Now, 37 Creator = new UserInfo() 38  { 39 40 ID = 1, 41 Birthday = DateTimeHelper.Now.AddYears(-100), 42 UserName = "yswenli" 43  }, 44 Users = new System.Collections.Generic.List<UserInfo>() 45  { 46 new UserInfo() 47  { 48 49 ID = 1, 50 Birthday = DateTimeHelper.Now.AddYears(-100), 51 UserName = "yswenli" 52  } 53  } 54  }; 55 56 var count = 100000; 57 var len1 = 0; 58 var len2 = 0; 59 60 Stopwatch sw = new Stopwatch(); 61  sw.Start(); 62 63 List<byte[]> list = new List<byte[]>(); 64 for (int i = 0; i < count; i++) 65  { 66 var bytes = SerializeBinary(groupInfo); 67 len1 = bytes.Length; 68  list.Add(bytes); 69  } 70 ConsoleHelper.WriteLine($"BinaryFormatter实体序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒"); 71 72  sw.Restart(); 73 for (int i = 0; i < count; i++) 74  { 75 var obj = DeSerializeBinary(list[i]); 76  } 77 ConsoleHelper.WriteLine($"BinaryFormatter实体反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒"); 78 ConsoleHelper.WriteLine($"BinaryFormatter序列化生成bytes大小:{len1 * count * 1.0 / 1024 / 1024} Mb"); 79  list.Clear(); 80  sw.Restart(); 81 82 for (int i = 0; i < count; i++) 83  { 84 var bytes = RPC.Serialize.ParamsSerializeUtil.Serialize(groupInfo); 85 len2 = bytes.Length; 86  list.Add(bytes); 87  } 88 ConsoleHelper.WriteLine($"ParamsSerializeUtil实体序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒"); 89  sw.Restart(); 90 for (int i = 0; i < count; i++) 91  { 92 int os = 0; 93 94 var obj = RPC.Serialize.ParamsSerializeUtil.Deserialize(groupInfo.GetType(), list[i], ref os); 95  } 96 ConsoleHelper.WriteLine($"ParamsSerializeUtil实体反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒"); 97 ConsoleHelper.WriteLine($"ParamsSerializeUtil序列化生成bytes大小:{len2 * count * 1.0 / 1024 / 1024} Mb"); 98  sw.Stop(); 99 }
View Code

        运行结果:

 

原文链接:https://yq.aliyun.com/articles/613899
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章