WCF绑定使用的序列化的性能testing
我有以下的对象:
public partial class Game { public bool Finished { get; set; } public Guid GameGUID { get; set; } public long GameID { get; set; } public bool GameSetup { get; set; } public Nullable<int> MaximumCardsInDeck { get; set; } public Player Player { get; set; } public Player Player1 { get; set; } public bool Player1Connected { get; set; } public bool Player1EnvironmentSetup { get; set; } public long Player1ID { get; set; } public int Player1Won { get; set; } public bool Player2Connected { get; set; } public bool Player2EnvironmentSetup { get; set; } public long Player2ID { get; set; } public int Player2Won { get; set; } public int Round { get; set; } public Nullable<int> RoundsToWin { get; set; } public bool Started { get; set; } public string StateXML { get; set; } public Nullable<DateTime> TimeEnded { get; set; } public Nullable<int> TimeLimitPerTurn { get; set; } public byte[] TimeStamp { get; set; } public Nullable<DateTime> TimeStarted { get; set; } }
这个课程将会被填充一些testing数据 。
我需要比较WCF服务的不同绑定使用的不同序列化器的性能 :
- basicHttpBinding => SoapFormatter (TextFormatter?)
- binaryBinding => BinaryFormatter
- XMLFormatter
我需要做的是:
- 获取正在序列化的对象的大小
- 在serizlization之后到达现在的大小
- 时间序列化
- 时间反序列化
我已经尝试了一些东西,但我挣扎了一下。 也许这种测量已经有了一些简单的代码。
好; 我会咬…这里有一些原始序列化指标(emph:你可能需要考虑base-64 / MTOM来获得整体的带宽需求,再加上WCF增加的任何固定开销(包括空间和CPU))。 结果第一:
BinaryFormatter Length: 1314 Serialize: 6746 Deserialize: 6268 XmlSerializer Length: 1049 Serialize: 3282 Deserialize: 5132 DataContractSerializer Length: 911 Serialize: 1411 Deserialize: 4380 NetDataContractSerializer Length: 1139 Serialize: 2014 Deserialize: 5645 JavaScriptSerializer Length: 528 Serialize: 12050 Deserialize: 30558 (protobuf-net v2) Length: 112 Serialize: 217 Deserialize: 250
(所以我总结protobuf-net v2获胜者…)
使用.NET 4.5和当前库构build更新的数字,在更新的机器上:
BinaryFormatter Length: 1313 Serialize: 2786 Deserialize: 2407 XmlSerializer Length: 1049 Serialize: 1265 Deserialize: 2165 DataContractSerializer Length: 911 Serialize: 574 Deserialize: 2011 NetDataContractSerializer Length: 1138 Serialize: 850 Deserialize: 2535 JavaScriptSerializer Length: 528 Serialize: 8660 Deserialize: 8468 (protobuf-net v2) Length: 112 Serialize: 78 Deserialize: 134
与testing装备(编译优化,在命令行运行):
(注意我必须发明Player
类和一些示例数据):
using System; using System.Diagnostics; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Web.Script.Serialization; using System.Xml.Serialization; using ProtoBuf.Meta; static class Program { static void Main() { var orig = new Game { Finished = true, GameGUID = Guid.NewGuid(), GameID = 12345, GameSetup = false, MaximumCardsInDeck = 20, Player = new Player { Name = "Fred"}, Player1 = new Player { Name = "Barney"}, Player1Connected = true, Player1EnvironmentSetup = true, Player1ID = 12345, Player1Won = 3, Player2Connected = true, Player2EnvironmentSetup = true, Player2ID = 23456, Player2Won = 0, Round = 4, RoundsToWin = 5, Started = true, StateXML = "not really xml", TimeEnded = null, TimeLimitPerTurn = 500, TimeStamp = new byte[] {1,2,3,4,5,6}, TimeStarted = DateTime.Today}; const int LOOP = 50000; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); using (var ms = new MemoryStream()) { var ser = new BinaryFormatter(); Console.WriteLine(); Console.WriteLine(ser.GetType().Name); ser.Serialize(ms, orig); Console.WriteLine("Length: " + ms.Length); ms.Position = 0; ser.Deserialize(ms); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ms.SetLength(0); ser.Serialize(ms, orig); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ser.Deserialize(ms); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); using (var ms = new MemoryStream()) { var ser = new XmlSerializer(typeof(Game)); Console.WriteLine(); Console.WriteLine(ser.GetType().Name); ser.Serialize(ms, orig); Console.WriteLine("Length: " + ms.Length); ms.Position = 0; ser.Deserialize(ms); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ms.SetLength(0); ser.Serialize(ms, orig); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ser.Deserialize(ms); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); using (var ms = new MemoryStream()) { var ser = new DataContractSerializer(typeof(Game)); Console.WriteLine(); Console.WriteLine(ser.GetType().Name); ser.WriteObject(ms, orig); Console.WriteLine("Length: " + ms.Length); ms.Position = 0; ser.ReadObject(ms); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ms.SetLength(0); ser.WriteObject(ms, orig); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ser.ReadObject(ms); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); using (var ms = new MemoryStream()) { var ser = new NetDataContractSerializer(); Console.WriteLine(); Console.WriteLine(ser.GetType().Name); ser.Serialize(ms, orig); Console.WriteLine("Length: " + ms.Length); ms.Position = 0; ser.Deserialize(ms); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ms.SetLength(0); ser.Serialize(ms, orig); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ser.Deserialize(ms); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); { var sb = new StringBuilder(); var ser = new JavaScriptSerializer(); Console.WriteLine(); Console.WriteLine(ser.GetType().Name); ser.Serialize(orig, sb); Console.WriteLine("Length: " + sb.Length); ser.Deserialize(sb.ToString(), typeof(Game)); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { sb.Length = 0; ser.Serialize(orig, sb); } watch.Stop(); string s = sb.ToString(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ser.Deserialize(s, typeof(Game)); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); using (var ms = new MemoryStream()) { var ser = CreateProto(); Console.WriteLine(); Console.WriteLine("(protobuf-net v2)"); ser.Serialize(ms, orig); Console.WriteLine("Length: " + ms.Length); ms.Position = 0; ser.Deserialize(ms, null, typeof(Game)); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ms.SetLength(0); ser.Serialize(ms, orig); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { ms.Position = 0; ser.Deserialize(ms, null, typeof(Game)); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } Console.WriteLine(); Console.WriteLine("All done; any key to exit"); Console.ReadKey(); } static TypeModel CreateProto() { var meta = TypeModel.Create(); meta.Add(typeof(Game), false).Add(Array.ConvertAll(typeof(Game).GetProperties(),prop=>prop.Name)); meta.Add(typeof(Player), false).Add(Array.ConvertAll(typeof(Player).GetProperties(),prop=>prop.Name)); return meta.Compile(); } } [Serializable, DataContract] public partial class Game { [DataMember] public bool Finished { get; set; } [DataMember] public Guid GameGUID { get; set; } [DataMember] public long GameID { get; set; } [DataMember] public bool GameSetup { get; set; } [DataMember] public Nullable<int> MaximumCardsInDeck { get; set; } [DataMember] public Player Player { get; set; } [DataMember] public Player Player1 { get; set; } [DataMember] public bool Player1Connected { get; set; } [DataMember] public bool Player1EnvironmentSetup { get; set; } [DataMember] public long Player1ID { get; set; } [DataMember] public int Player1Won { get; set; } [DataMember] public bool Player2Connected { get; set; } [DataMember] public bool Player2EnvironmentSetup { get; set; } [DataMember] public long Player2ID { get; set; } [DataMember] public int Player2Won { get; set; } [DataMember] public int Round { get; set; } [DataMember] public Nullable<int> RoundsToWin { get; set; } [DataMember] public bool Started { get; set; } [DataMember] public string StateXML { get; set; } [DataMember] public Nullable<DateTime> TimeEnded { get; set; } [DataMember] public Nullable<int> TimeLimitPerTurn { get; set; } [DataMember] public byte[] TimeStamp { get; set; } [DataMember] public Nullable<DateTime> TimeStarted { get; set; } } [Serializable, DataContract] public class Player { [DataMember] public string Name { get; set; } }
我也有.NET中的不同序列化器的基准图 ,显示@Marc Gravell的二进制protobuf-net序列化器是明显的赢家。 尽pipe我保持最快的文本序列化器.NET,但它比最接近匹配的文本序列化器快得多,也比.NET中的所有序列化器都快得多。
这些基准基于来自Microsoft的Nortwind示例数据库,并显示每个序列化程序与Protobuf-net的比较速度。
ProtoBuf.net(v1) 1x ServiceStack TypeSerializer 2.23x ServiceStack JsonSerializer 2.58x Microsoft DataContractSerializer 6.93x NewtonSoft.Json 7.83x Microsoft BinaryFormatter 9.21x Microsoft JsonDataContractSerializer 9.31x
完整的基准可以在这里find
因此,如果您喜欢/需要使用快速文本序列化器,则可以使用以下链接到Service Stack的开源文本序列化器:
- JSON序列化器
- JSV串行器
顺便说一句,微软的JavaScriptSerializerperformance出最差的性能,有时比protobufnetworking慢了40倍到100倍。 拿出来,因为他们正在放慢我的基准:)
我已经修改了@ Marc的基准源代码,并添加了ServiceStack的JSV和JSON串行器的结果以下是我的3yo iMac上的结果:
BinaryFormatter Length: 1313 Serialize: 3959 Deserialize: 3395 XmlSerializer Length: 1049 Serialize: 1710 Deserialize: 2716 DataContractSerializer Length: 911 Serialize: 712 Deserialize: 2117 NetDataContractSerializer Length: 1138 Serialize: 1093 Deserialize: 4825 TypeSerializer Length: 431 Serialize: 496 Deserialize: 887 JsonSerializer Length: 507 Serialize: 558 Deserialize: 1213
这里是我添加到上面@ Marc的基准testing的源代码。
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); var sbJsv = new StringBuilder(4096); using (var sw = new StringWriter(sbJsv)) { Console.WriteLine(); Console.WriteLine(typeof(TypeSerializer).Name); TypeSerializer.SerializeToWriter(orig, sw); var jsv = sbJsv.ToString(); Console.WriteLine("Length: " + sbJsv.Length); TypeSerializer.DeserializeFromString<Game>(jsv); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { sbJsv.Length = 0; TypeSerializer.SerializeToWriter(orig, sw); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { TypeSerializer.DeserializeFromString<Game>(jsv); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); } GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); var sbJson = new StringBuilder(4096); using (var sw = new StringWriter(sbJson)) { Console.WriteLine(); Console.WriteLine(typeof(JsonSerializer).Name); JsonSerializer.SerializeToWriter(orig, sw); var json = sbJson.ToString(); Console.WriteLine("Length: " + sbJson.Length); JsonSerializer.DeserializeFromString<Game>(json); var watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { sbJson.Length = 0; JsonSerializer.SerializeToWriter(orig, sw); } watch.Stop(); Console.WriteLine("Serialize: " + watch.ElapsedMilliseconds); watch = Stopwatch.StartNew(); for (int i = 0; i < LOOP; i++) { JsonSerializer.DeserializeFromString<Game>(json); } watch.Stop(); Console.WriteLine("Deserialize: " + watch.ElapsedMilliseconds); }
注意:我无法获得他所使用的@ Marc的protobuf-net v2 r352 dlls,因此我必须对protobuf-net基准进行评论。
在最简单的层面上; 简单地序列化数据的卸载,计时,并测量带宽。 卸载应包括大和小(但很多)的有效载荷。
你也应该考虑有/没有MTOM。 虽然我也许有偏见,我build议你应该包括替代的WCF序列化程序,如protobuf网(让我知道如果你需要帮助挂钩)。 从该地区的大量工作中,通常每一项措施都会以一个合理的利润率击败你所提到的那些。
这里所涉及的大部分内容都可以在序列化程度级别上进行调查,甚至不需要触及WCF,但是,base-64 / MTOM并不是100%的图像。
但是,我们不能为您定义您的措施。 只有你可以决定什么是关键。 我确实有一些措施 – 通常是简单的:
- 序列化一次MemorySteam(和反序列化); 这让你的尺寸和总理的JIT
- 现在保持内存stream(作为一个方便的缓冲区),并(秒表内),如果时间序列化数以千计。 划分。 每次倒带,以便覆盖(不扩展)。
- 重复但反序列化数千次。 划分。
使用一个大小不变的对象; 从types获取“大小”信息是混乱的,并不会让你知道哪一个是最好的。 任何装饰为DataContract的对象都可以序列化为二进制(DataContractinheritanceSerializable),基本XML(任何具有默认构造函数的对象都可以被序列化为XML)或DataContract XML(这需要最开始的标记,但是非常简单) 。
对于正在运行的testing,请创build一个将使用Object和Serializer的方法。 它应该创build一个MemoryStream并启动一个StopWatch,然后将该对象序列化到MemoryStream中(确保Flush())。 然后停止秒表,并返回结果作为TimeSpan,以及Stream的长度。 然后重置并启动Stopwatch并反序列化Stream,并记住那个时间。 您可以将返回结果设置为一个简单的结构。
为每个想要testing的序列化程序运行相同的对象。 输出每个结果到控制台或debugging输出,并可能最好的序列化器赢。
一般来说,我想你会发现:
-
BinarySerializer将是最快和最小的,因为它在序列化时具有最less的字节开销。 但是,.NET的二进制序列化是特定于平台的; 如果你想谈谈除了另一个知道你的确切types的.NET程序集以外的任何东西,那就把它忘掉吧。
-
XMLSerializer,SoapSerializer和DataContractSerializer都输出各种forms的XML。 DataContract实际上是最简单的格式(XML非常基本,因为握手和其他协议/通信信息是独立的),并且可能会相当快。 由于传输和元数据信息,SOAP在序列化文件中有很多膨胀,但由于格式非常严格,所以很容易生成。 基本的XML序列化,因为它非常灵活,有很多开销,但可以生成一个非常简单或非常复杂的模式。