I have the following object:
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; }
}
This class gonna be filled with some test data.
I need the to compare the Performance of different Serializers used by the different forms of bindings for WCF Services:
What i need to do in detail is:
I already tried some stuff, but i am struggling a bit. Maybe there is already some simple code for this kind of measurement.
But, although manual testing is necessary, using this approach to test the basic functionality of a WCF service would be time consuming, error prone, inefficient, and just plain boring. A better approach would be to write test automation similar to the program shown running in Figure 2.
A WCF binding is a collection of information that specifies how a WCF service communicates with clients including the transport protocol used by the service, the text encoding scheme used, and so on. You can use built-in bindings or create custom bindings.
Here I am trying to highlight some basic points before adopting WCF: 1. Don’t rebuild existing application just for the sake of using WCF. 2. Do develop a detailed version of how one’s software should identically be constricted that take into the account. 3. Do consider how to integrate one’s existing system with new services.
In essence, the test harness program is just a WCF client that sends an input message to the WCF service under test and verifies a correct return message. I begin by launching a new instance of Visual Studio and creating a C# console application program named TestHarness.
OK; I'll bite... here's some raw serializer metrics (emph: you may need to consider base-64/MTOM to get overall bandwidth requirements, plus whatever fixed overheads (both space and CPU) that WCF adds), however; results first:
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
(so I conclude protobuf-net v2 the winner...)
Numbers updated with .NET 4.5 and current library builds, on a newer machine:
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
with test rig (compiled with optimizations, run at command line):
(and note I had to invent the Player
class and some sample data):
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; }
}
I also have benchmarks graphs for different serializers in .NET that show @Marc Gravell's binary protobuf-net serializer as the clear winner. Although I maintain the fastest text serializers .NET which come closest to matching it and are also much faster than all the serializers that come in BCL in .NET.
These benchmarks are based on the Nortwind sample Database from Microsoft and show how much slower each serializer is compared to Protobuf-net's.
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
The full benchmarks are available here
So if you prefer/need to use a fast text-serializer here are links to Service Stack's open source text serializers:
By the way the Microsoft's JavaScriptSerializer showed the worst performance and at times were 40x-100x slower than protobuf-nets. Took it out because they were slowing down my benchmarks :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With