views:

614

answers:

1

the title says it all. i noticed that serialization as well des deserialization takes 10x as lang with protobuf-net compared to XmlSerializer. the output files however are much smaller. i find this confusing because protobuf is such a simple format that should run very fast.

any hints on how to speed it are greatly appreciated.

Edit: here is the code:

[ProtoContract]
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 IList<BinaryNameValue> RequestCookies;
    [ProtoMember(28)]
    public IList<BinaryNameValue> ResponseCookies;
    [ProtoMember(29)]
    public IList<BinaryNameValue> RequestHeaders;
    [ProtoMember(30)]
    public IList<BinaryNameValue> RequestPostData;
    [ProtoMember(31)]
    public IList<BinaryNameValue> AdditionalInfo;
}

Serializer.SerializeWithLengthPrefix(stream, request, PrefixStyle.Base128);
+4  A: 

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);
    }
}
Marc Gravell
Thank you! I will create a small repro solution and upload it to rapidshare.
usr
I'll look forward to it. Make sure you add a comment here when you do, or I won't see it.
Marc Gravell