2

[更新#1] :如果其他人有兴趣查看基准测试,我已将修改和修复的“演示”项目上传到https://github.com/sidshetye/SerializersCompare 。

[Update#2]:我看到 ProtoBufs 仅在后续迭代中领先数量级。对于一次性序列化,BinaryFormatter 是一个快一个数量级的序列化。为什么?单独的问题...

我正在尝试比较 BinaryFormatter、Json.NET 和 ProtoBuf.NET(今天从 NuGet 获得后者)。我发现 ProtoBuf 没有输出任何真实字段,全是空值和 0(见下文)。加上 BinaryFormatter 似乎要快得多。我基本上序列化 => 反序列化对象并进行比较

  • 带有重新生成对象的原始对象
  • 字节大小
  • 以毫秒为单位的时间

问题

  1. 我怎样才能让 ProtoBuf 实际吐出真实值而不仅仅是(默认?)值?
  2. 我在速度方面做错了什么?我认为 ProtoBuf 应该是最快的序列化程序?

我从我的测试应用程序得到的输出如下:

Json: Objects identical
Json in UTF-8: 180 bytes, 249.7054 ms

BinaryFormatter: Objects identical
BinaryFormatter: 512 bytes, 1.7864 ms

ProtoBuf: Original and regenerated objects differ !!
====Regenerated Object====
{
    "functionCall": null,
    "parameters": null,
    "name": null,
    "employeeId": 0,
    "raiseRate": 0.0,
    "addressLine1": null,
    "addressLine2": null
}
ProtoBuf: 256 bytes, 117.969 ms

我的测试是在控制台应用程序中使用一个简单的实体(见下文)。系统:Windows 8x64,VS2012 更新 1,.NET4.5。顺便说一句,我使用[ProtoContract]and[ProtoMember(X)]约定得到了相同的结果。文档不清楚,但似乎DataContract 是较新的“统一”支持约定(对吗?)

[Serializable]
[DataContract]
class SimpleEntity
{
    [DataMember(Order = 1)]
    public string functionCall {get;set;}

    [DataMember(Order = 2)]
    public string parameters { get; set; }

    [DataMember(Order = 3)]
    public string name { get; set; }

    [DataMember(Order = 4)]
    public int employeeId { get; set; }

    [DataMember(Order = 5)]
    public float raiseRate { get; set; }

    [DataMember(Order = 6)]
    public string addressLine1 { get; set; }

    [DataMember(Order = 7)]
    public string addressLine2 { get; set; }

    public SimpleEntity()
    {
    }

    public void FillDummyData()
    {
        functionCall = "FunctionNameHere";
        parameters = "x=1,y=2,z=3";

        name = "Mickey Mouse";
        employeeId = 1;
        raiseRate = 1.2F;
        addressLine1 = "1 Disney Street";
        addressLine2 = "Disneyland, CA";
    }
}

对于那些感兴趣的人,这里是我的 ProtoBufs 的 AllSerializers 类的片段

public byte[] SerProtoBuf(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Serializer.Serialize(ms, thisObj);
        return ms.GetBuffer();
    }
}

public T DeserProtoBuf<T>(byte[] bytes)
{

    using (MemoryStream ms = new MemoryStream())
    {
        ms.Read(bytes, 0, bytes.Count());
        return Serializer.Deserialize<T>(ms);
    }
}
4

1 回答 1

2

Firstly, your serialize / deserialize methods are both broken; you are over-reporting the result (GetBuffer(), without Length), and you aren't writing anything into the stream for deserialization. Here's a correct implementation (although you could also use GetBuffer() if you were returning ArraySegment<byte>):

public static byte[] SerProtoBuf(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Serializer.NonGeneric.Serialize(ms, thisObj);
        return ms.ToArray();
    }
}

public static T DeserProtoBuf<T>(byte[] bytes)
{
    using (MemoryStream ms = new MemoryStream(bytes))
    {
        return Serializer.Deserialize<T>(ms);
    }
}

That is why you are getting no data back. Secondly, you don't say how you are timing it, so here's some I've written based on your code (which also includes code to show that it is getting all the values back). Results first:

Via BinaryFormatter:
1 Disney Street
Disneyland, CA
1
FunctionNameHere
Mickey Mouse
x=1,y=2,z=3
1.2

Via protobuf-net:
1 Disney Street
Disneyland, CA
1
FunctionNameHere
Mickey Mouse
x=1,y=2,z=3
1.2

Serialize BinaryFormatter: 112 ms, 434 bytes
Deserialize BinaryFormatter: 113 ms
Serialize protobuf-net: 14 ms, 85 bytes
Deserialize protobuf-net: 19 ms

Analysis:

Both serializers stored the same data; protobuf-net was an order of magnitude faster, and a factor of 5 smaller output. I declare: winner.

Code:

static BinaryFormatter bf = new BinaryFormatter();
public static byte[] SerBinaryFormatter(object thisObj)
{
    using (MemoryStream ms = new MemoryStream())
    {
        bf.Serialize(ms, thisObj);
        return ms.ToArray();
    }
}

public static T DeserBinaryFormatter<T>(byte[] bytes)
{
    using (MemoryStream ms = new MemoryStream(bytes))
    {
        return (T)bf.Deserialize(ms);
    }
}
static void Main()
{
    SimpleEntity obj = new SimpleEntity(), clone;
    obj.FillDummyData();

    // test that we get non-zero bytes
    var data = SerBinaryFormatter(obj);
    clone = DeserBinaryFormatter<SimpleEntity>(data);
    Console.WriteLine("Via BinaryFormatter:");
    Console.WriteLine(clone.addressLine1);
    Console.WriteLine(clone.addressLine2);
    Console.WriteLine(clone.employeeId);
    Console.WriteLine(clone.functionCall);
    Console.WriteLine(clone.name);
    Console.WriteLine(clone.parameters);
    Console.WriteLine(clone.raiseRate);
    Console.WriteLine();

    data = SerProtoBuf(obj);
    clone = DeserProtoBuf<SimpleEntity>(data);
    Console.WriteLine("Via protobuf-net:");
    Console.WriteLine(clone.addressLine1);
    Console.WriteLine(clone.addressLine2);
    Console.WriteLine(clone.employeeId);
    Console.WriteLine(clone.functionCall);
    Console.WriteLine(clone.name);
    Console.WriteLine(clone.parameters);
    Console.WriteLine(clone.raiseRate);
    Console.WriteLine();

    Stopwatch watch = new Stopwatch();
    const int LOOP = 10000;

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        data = SerBinaryFormatter(obj);
    }
    watch.Stop();
    Console.WriteLine("Serialize BinaryFormatter: {0} ms, {1} bytes", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        clone = DeserBinaryFormatter<SimpleEntity>(data);
    }
    watch.Stop();
    Console.WriteLine("Deserialize BinaryFormatter: {0} ms", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        data = SerProtoBuf(obj);
    }
    watch.Stop();
    Console.WriteLine("Serialize protobuf-net: {0} ms, {1} bytes", watch.ElapsedMilliseconds, data.Length);

    watch.Reset();
    watch.Start();
    for (int i = 0; i < LOOP; i++)
    {
        clone = DeserProtoBuf<SimpleEntity>(data);
    }
    watch.Stop();
    Console.WriteLine("Deserialize protobuf-net: {0} ms", watch.ElapsedMilliseconds, data.Length);
}

Lastly, [DataMember(...)] support isn't really the "newer 'uniformly' support convention" - it certainly isn't "newer" - I'm pretty sure it has supported both of those since something like commit #4 (and possibly earlier). It is just options provided for convenience:

  • not all target platforms have DataMemberAttribute
  • some people prefer to limit the DTO layer to inbuilt markers
  • some types are largely outside your control, but may already have those markers (generated data from LINQ-to-SQL, for example)
  • additionally, note that 2.x allows you to define the model at runtime without having to add attributes (although attributes remain the most convenient way to do it)
于 2012-12-05T07:49:19.377 回答