C#高性能二进制序列化
二进制序列化可以方便快捷的将对象进行持久化或者网络传输,并且体积小、性能高,应用面甚至还要高于json的序列化;开始之前,先来看看dotcore/dotne自带的二进制序列化:C#中对象序列化和反序列化一般是通过BinaryFormatter类来实现的二进制序列化、反序列化的。
BinaryFormatter序列化:
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); System.IO.MemoryStream memStream = new System.IO.MemoryStream(); serializer.Serialize(memStream, request);
BinaryFormatter反序列化:
memStream.Position=; System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); object newobj = deserializer.Deserialize(memStream); memStream.Close(); 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、反射等来实现序列化与反序列化;
序列化实现如下:
public static byte[] Serialize(object param)
{
List<byte> datas = new List<byte>(); var len = ; byte[] data = null; if (param == null)
{
len = ;
}
else
{
if (param is string)
{
data = Encoding.UTF8.GetBytes((string)param);
}
else if (param is byte)
{
data = new byte[] { (byte)param };
}
else if (param is bool)
{
data = BitConverter.GetBytes((bool)param);
}
else if (param is short)
{
data = BitConverter.GetBytes((short)param);
}
else if (param is int)
{
data = BitConverter.GetBytes((int)param);
}
else if (param is long)
{
data = BitConverter.GetBytes((long)param);
}
else if (param is float)
{
data = BitConverter.GetBytes((float)param);
}
else if (param is double)
{
data = BitConverter.GetBytes((double)param);
}
else if (param is DateTime)
{
var str = "wl" + ((DateTime)param).Ticks;
data = Encoding.UTF8.GetBytes(str);
}
else if (param is Enum)
{
var enumValType = Enum.GetUnderlyingType(param.GetType()); if (enumValType == typeof(byte))
{
data = new byte[] { (byte)param };
}
else if (enumValType == typeof(short))
{
data = BitConverter.GetBytes((Int16)param);
}
else if (enumValType == typeof(int))
{
data = BitConverter.GetBytes((Int32)param);
}
else
{
data = BitConverter.GetBytes((Int64)param);
}
}
else if (param is byte[])
{
data = (byte[])param;
}
else
{
var type = param.GetType(); if (type.IsGenericType || type.IsArray)
{
if (TypeHelper.DicTypeStrs.Contains(type.Name))
data = SerializeDic((System.Collections.IDictionary)param);
else if (TypeHelper.ListTypeStrs.Contains(type.Name) || type.IsArray)
data = SerializeList((System.Collections.IEnumerable)param);
else
data = SerializeClass(param, type);
}
else if (type.IsClass)
{
data = SerializeClass(param, type);
} }
if (data != null)
len = data.Length;
}
datas.AddRange(BitConverter.GetBytes(len));
if (len > )
{
datas.AddRange(data);
}
return datas.Count == ? null : datas.ToArray();
}
反序列化实现如下:
public static object Deserialize(Type type, byte[] datas, ref int offset)
{
dynamic obj = null; var len = ; byte[] data = null; len = BitConverter.ToInt32(datas, offset);
offset += ;
if (len > )
{
data = new byte[len];
Buffer.BlockCopy(datas, offset, data, , len);
offset += len; if (type == typeof(string))
{
obj = Encoding.UTF8.GetString(data);
}
else if (type == typeof(byte))
{
obj = (data);
}
else if (type == typeof(bool))
{
obj = (BitConverter.ToBoolean(data, ));
}
else if (type == typeof(short))
{
obj = (BitConverter.ToInt16(data, ));
}
else if (type == typeof(int))
{
obj = (BitConverter.ToInt32(data, ));
}
else if (type == typeof(long))
{
obj = (BitConverter.ToInt64(data, ));
}
else if (type == typeof(float))
{
obj = (BitConverter.ToSingle(data, ));
}
else if (type == typeof(double))
{
obj = (BitConverter.ToDouble(data, ));
}
else if (type == typeof(decimal))
{
obj = (BitConverter.ToDouble(data, ));
}
else if (type == typeof(DateTime))
{
var dstr = Encoding.UTF8.GetString(data);
var ticks = long.Parse(dstr.Substring());
obj = (new DateTime(ticks));
}
else if (type.BaseType == typeof(Enum))
{
var numType = Enum.GetUnderlyingType(type); if (numType == typeof(byte))
{
obj = Enum.ToObject(type, data[]);
}
else if (numType == typeof(short))
{
obj = Enum.ToObject(type, BitConverter.ToInt16(data, ));
}
else if (numType == typeof(int))
{
obj = Enum.ToObject(type, BitConverter.ToInt32(data, ));
}
else
{
obj = Enum.ToObject(type, BitConverter.ToInt64(data, ));
}
}
else if (type == typeof(byte[]))
{
obj = (byte[])data;
}
else if (type.IsGenericType)
{
if (TypeHelper.ListTypeStrs.Contains(type.Name))
{
obj = DeserializeList(type, data);
}
else if (TypeHelper.DicTypeStrs.Contains(type.Name))
{
obj = DeserializeDic(type, data);
}
else
{
obj = DeserializeClass(type, data);
}
}
else if (type.IsClass)
{
obj = DeserializeClass(type, data);
}
else if (type.IsArray)
{
obj = DeserializeArray(type, data);
}
else
{
throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定义的类型:" + type.ToString());
} }
return obj;
}
其他详细的代码可以查看https://github.com/yswenli/SAEA/blob/master/Src/SAEA.RPC/Serialize/ParamsSerializeUtil.cs
功能基本实现了,下面对比一下10000次的实体序列化与反序列化测试结果:
实体代码:
var groupInfo = new GroupInfo()
{
GroupID = ,
IsTemporary = false,
Name = "yswenli group",
Created = DateTimeHelper.Now,
Creator = new UserInfo()
{ ID = ,
Birthday = DateTimeHelper.Now.AddYears(-),
UserName = "yswenli"
},
Users = new System.Collections.Generic.List<UserInfo>()
{
new UserInfo()
{ ID = ,
Birthday = DateTimeHelper.Now.AddYears(-),
UserName = "yswenli"
}
}
};
测试代码:
public static byte[] SerializeBinary(object request)
{ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); using (System.IO.MemoryStream memStream = new System.IO.MemoryStream())
{
serializer.Serialize(memStream, request); return memStream.ToArray();
}
} public static object DeSerializeBinary(byte[] data)
{
using (System.IO.MemoryStream memStream = new System.IO.MemoryStream(data))
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); return deserializer.Deserialize(memStream);
}
} static void SerializeTest()
{
var groupInfo = new GroupInfo()
{
GroupID = ,
IsTemporary = false,
Name = "yswenli group",
Created = DateTimeHelper.Now,
Creator = new UserInfo()
{ ID = ,
Birthday = DateTimeHelper.Now.AddYears(-),
UserName = "yswenli"
},
Users = new System.Collections.Generic.List<UserInfo>()
{
new UserInfo()
{ ID = ,
Birthday = DateTimeHelper.Now.AddYears(-),
UserName = "yswenli"
}
}
}; var count = ;
var len1 = ;
var len2 = ; Stopwatch sw = new Stopwatch();
sw.Start(); List<byte[]> list = new List<byte[]>();
for (int i = ; i < count; i++)
{
var bytes = SerializeBinary(groupInfo);
len1 = bytes.Length;
list.Add(bytes);
}
ConsoleHelper.WriteLine($"BinaryFormatter实体序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒"); sw.Restart();
for (int i = ; i < count; i++)
{
var obj = DeSerializeBinary(list[i]);
}
ConsoleHelper.WriteLine($"BinaryFormatter实体反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
ConsoleHelper.WriteLine($"BinaryFormatter序列化生成bytes大小:{len1 * count * 1.0 / 1024 / 1024} Mb");
list.Clear();
sw.Restart(); for (int i = ; i < count; i++)
{
var bytes = RPC.Serialize.ParamsSerializeUtil.Serialize(groupInfo);
len2 = bytes.Length;
list.Add(bytes);
}
ConsoleHelper.WriteLine($"ParamsSerializeUtil实体序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
sw.Restart();
for (int i = ; i < count; i++)
{
int os = ; var obj = RPC.Serialize.ParamsSerializeUtil.Deserialize(groupInfo.GetType(), list[i], ref os);
}
ConsoleHelper.WriteLine($"ParamsSerializeUtil实体反序列化平均:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
ConsoleHelper.WriteLine($"ParamsSerializeUtil序列化生成bytes大小:{len2 * count * 1.0 / 1024 / 1024} Mb");
sw.Stop();
}
运行结果:
更多内容,请Fork或Star我的github:https://github.com/yswenli/SAEA
C#高性能二进制序列化的更多相关文章
- 开源!一款功能强大的高性能二进制序列化器Bssom.Net
好久没更新博客了,我开源了一款高性能的二进制序列化器Bssom.Net和新颖的二进制协议Bssom,欢迎大家Star,欢迎参与项目贡献! Net开源技术交流群 976304396,禁止水,只能讨论技术 ...
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
- REST RPC HTTP vs 高性能二进制协议 序列化和通信协议
edisonchou https://mp.weixin.qq.com/s/-XZXqXawR-NxJMPCeiNsmg .NET Core微服务之服务间的调用方式(REST and RPC) Edi ...
- 性能超四倍的高性能.NET二进制序列化库
二进制序列化在.NET中有很多使用场景,如我们使用分布式缓存时,通常将缓存对象序列化为二进制数据进行缓存,在ASP.NET中,很多中间件(如认证等)也都是用了二进制序列化. 在.NET中我们通常使用S ...
- 二进制序列化框架easypack发布啦!
简介 easypack是基于boost.serialization的二进制序列化框架,使用极其方便. Examples 基本类型 int age = 20; std::string name = &q ...
- C# 序列化(二)二进制序列化的案例
这篇是针对上一篇讲序列化的文章的一个实际案例,WinForm程序的主界面如下:
- C#中的二进制序列化和Json序列化
序列化就是把一个对象变成流的形式,方便传输和还原.小弟不才,总结下对二进制序列化和Json序列化的使用: 1.首先,二进制序列化(BinaryFormatter)要求要序列化的类必须是可序列化的(即在 ...
- java编解码技术,json序列化与二进制序列化
1.何为json序列化与二进制序列化 通常我们在程序中采用的以json为传输,将json转为对象的就是json序列化了.而二进制序列化通常是我们将数据转换为二进制进行传输,然后在进行各类转换操作 2. ...
- .net下二进制序列化的格式分析[转]
.net下二进制序列化的格式分析[转] -- 综合应用 (http://www.Host01.Com/article/Net/00020003/) --- .net下二进制序列化的格式分析 (http ...
随机推荐
- Nginx http相关常用配置总结
Nginx http相关常用配置总结 by:授客 QQ:1033553122 测试环境 nginx-1.10.0 client_max_body_size Syntax: client_ma ...
- Android为TV端助力 双缓存机制
废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...
- [20190312]关于增量检查点的疑问(补充).txt
[20190312]关于增量检查点的疑问(补充).txt --//有人问我以前写一个帖子的问题,关于增量检查点的问题,链接如下:http://blog.itpub.net/267265/viewspa ...
- Navicat Premium 连接oracle ORA-01017:用户名/口令无效;登陆被拒绝
解决的方法就是将用户名改成system
- java笔记--对信号量Semaphore的理解与运用
java Semaphore 信号量的使用: 在java中,提供了信号量Semaphore的支持. Semaphore类是一个计数信号量,必须由获取它的线程释放, 通常用于限制可以访问某些资源(物理或 ...
- IDLE提供的常用快捷键
IDLE提供的常用快捷键 快捷键 说明 适用于 F1 打开Python帮助文档 Python文件窗口和shell窗口均可用 Alt+P 浏览历史命令(上一条) 仅Python Shell窗口可用 Al ...
- c/c++ 友元的简单应用
友元的简单应用 1,对象 + 对象,或者,对象 + 数字,可以用类的成员函数去重载+号函数,但是,数字 + 对象就不能用类的成员函数去重载+号函数了, 因为编译器会把数字 + 对象翻译成数字.oper ...
- CentOS 7.0下安装Python3.6
CentOS 7.0自带Python2.7 安装Python3.6步骤 1.安装依赖 yum install -y zlib-devel bzip2-devel openssl-devel ncurs ...
- jar包导入导出
java项目: 在classLoader加载jar和class的时候,是分开加载的,一般jar导入分两种: 1.在web-inf下的lib中直接引入 2.在user library上引入 无论以上哪种 ...
- 为JQuery EasyUI 表单组件加上“清除”功能
1.背景 在使用 EasyUI 各表单组件时,尤其是使用 ComboBox(下拉列表框).DateBox(日期输入框).DateTimeBox(日期时间输入框)这三个组件时,经常有这样的需求,下拉框或 ...