The example you post is not enough to reliably reproduce things - not least it doesn't show any sample data, how you are measuring it, etc - and in particular because you haven't included BinaryNameValue
(I made some assumptions...).
Indeed, XmlSerializer
refuses to work with the non-public types, and the interface-only IList<T>
declarations, so I've made some modifications (see below) to make this a reasonable test. I've looped 50000 times each, using similar loops and GC
conditions (all visible below), and my results are (showing times for serialize/deserialize):
protobuf-net (ms): 1069/1727
XmlSerializer (ms): 3271/5218
So protobuf-net is noticeably quicker at both operations. Additionally, protobuf-net used 343 bytes (raw1
) compared to 1933 bytes for XmlSerializer
(raw2
).
In short; "cannot reproduce". Full test rig:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using ProtoBuf;
[ProtoContract]
public class BinaryRequest
{
[ProtoMember(1, IsRequired = true)]
public Guid ID;
[ProtoMember(2)]
public long? BeginDate;
[ProtoMember(3)]
public long? EndDate;
[ProtoMember(4)]
public Guid? UserID;
[ProtoMember(5)]
public string UserName;
[ProtoMember(6)]
public string RawUrl;
[ProtoMember(7)]
public string Path;
[ProtoMember(8)]
public string PhysicalPath;
[ProtoMember(9)]
public string FilePath;
[ProtoMember(10)]
public string Host;
[ProtoMember(11)]
public string UrlReferer;
[ProtoMember(12)]
public string UserHostAddress;
[ProtoMember(13)]
public string UserHostName;
[ProtoMember(14)]
public string UserAgent;
[ProtoMember(15)]
public string UserLanguages;
[ProtoMember(16)]
public string HttpMethod;
[ProtoMember(17)]
public string ResponseCacheControl;
[ProtoMember(18)]
public string ResponseCharset;
[ProtoMember(19)]
public string ResponseContentEncoding;
[ProtoMember(20)]
public string ResponseContentType;
[ProtoMember(21)]
public long? ResponseExpires;
[ProtoMember(22)]
public int? ResponseStatus;
[ProtoMember(23)]
public string ResponseStatusDescription;
[ProtoMember(24)]
public string Error;
[ProtoMember(25)]
public string ClientType;
[ProtoMember(26)]
public string ContentType;
[ProtoMember(27)]
public List<BinaryNameValue> RequestCookies;
[ProtoMember(28)]
public List<BinaryNameValue> ResponseCookies;
[ProtoMember(29)]
public List<BinaryNameValue> RequestHeaders;
[ProtoMember(30)]
public List<BinaryNameValue> RequestPostData;
[ProtoMember(31)]
public List<BinaryNameValue> AdditionalInfo;
}
[ProtoContract]
public class BinaryNameValue
{
[ProtoMember(1)]
public string Name;
[ProtoMember(2)]
public string Value;
public BinaryNameValue() { }
public BinaryNameValue(string name, string value) { Name = name; Value = value; }
}
static class Program
{
static void Main()
{
var request = new BinaryRequest
{
ID = Guid.NewGuid(),
AdditionalInfo = new List<BinaryNameValue> {
new BinaryNameValue("abc","def")
},
BeginDate = 12345678,
ClientType = "client type",
ContentType = "content type",
EndDate = 12345678,
Error = "error",
FilePath = "foo/bar",
Host = "host",
HttpMethod = "method",
Path = "path",
PhysicalPath = "physical path",
RawUrl = "raw url",
RequestCookies = new List<BinaryNameValue> {
new BinaryNameValue("ghi", "jkl")
},
RequestHeaders = new List<BinaryNameValue> {
new BinaryNameValue("mno", "pqr")
},
RequestPostData = new List<BinaryNameValue> {
new BinaryNameValue("stu","vwx")
},
ResponseCacheControl = "cache control",
ResponseCharset = "charset",
ResponseContentEncoding = "encoding",
ResponseContentType = "resp content type",
ResponseCookies = new List<BinaryNameValue> {
new BinaryNameValue("yza", "bcd")
},
ResponseExpires = 12345678,
ResponseStatus = 200,
ResponseStatusDescription = "OK",
UrlReferer = "http://stackoverflow.com",
UserAgent = "chrome",
UserHostAddress = "127.0.0.1",
UserHostName = "laptop",
UserID = Guid.NewGuid(),
UserLanguages = "C#",
UserName = "Marc"
};
const int LOOP = 50000;
byte[] raw1 = null, raw2 = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var watch1 = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
using (MemoryStream stream = new MemoryStream())
{
Serializer.SerializeWithLengthPrefix(stream, request, PrefixStyle.Base128);
if (i == 0) raw1 = stream.ToArray();
}
}
watch1.Stop();
MemoryStream stream_in1 = new MemoryStream(raw1);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var watch2 = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
stream_in1.Position = 0;
var obj = Serializer.DeserializeWithLengthPrefix<BinaryRequest>(
stream_in1, PrefixStyle.Base128);
}
watch2.Stop();
XmlSerializer ser = new XmlSerializer(request.GetType());
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var watch3 = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
using (MemoryStream stream = new MemoryStream())
{
ser.Serialize(stream, request);
if (i == 0) raw2 = stream.ToArray();
}
}
watch3.Stop();
MemoryStream stream_in2 = new MemoryStream(raw2);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var watch4 = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
stream_in2.Position = 0;
var obj = ser.Deserialize(stream_in2);
}
watch4.Stop();
Console.WriteLine("protobuf-net (ms): " + watch1.ElapsedMilliseconds
+ "/" + watch2.ElapsedMilliseconds);
Console.WriteLine("XmlSerializer (ms): " + watch3.ElapsedMilliseconds
+ "/" + watch4.ElapsedMilliseconds);
}
}