【问题标题】:ProtoBuf serialization missing data even for simple entities即使对于简单实体,ProtoBuf 序列化也缺少数据
【发布时间】:2012-12-06 08:45:13
【问题描述】:

[Update#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][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";
    }
}

对这里感兴趣的人是我的 AllSerializers 类的 ProtoBufs 的 sn-p

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);
    }
}

【问题讨论】:

    标签: serialization protobuf-net


    【解决方案1】:

    首先,您的序列化/反序列化方法都被破坏了;您过度报告了结果(GetBuffer(),没有Length),并且您没有将任何内容写入流以进行反序列化。这是一个正确的实现(虽然如果你返回ArraySegment&lt;byte&gt;,你也可以使用GetBuffer()):

    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);
        }
    }
    

    这就是为什么您没有得到任何数据。其次,你没有说你是如何计时的,所以这是我根据你的代码编写的一些代码(其中还包括显示它正在获取所有值的代码)。结果优先:

    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
    

    分析:

    两个序列化器存储相同的数据; protobuf-net 快了一个数量级,输出小了 5 倍。我宣布:获胜者。

    代码:

    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);
    }
    

    最后,[DataMember(...)] 支持并不是真正的“更新的‘统一’支持约定”——它肯定不是“更新的”——我很确定它已经支持这两个,因为类似于 commit #4 (并且可能更早)。这只是为了方便而提供的选项:

    • 并非所有目标平台都有DataMemberAttribute
    • 有些人喜欢将 DTO 层限制为内置标记
    • 某些类型在很大程度上超出了您的控制范围,但可能已经具有这些标记(例如,从 LINQ-to-SQL 生成的数据)
    • 另外,请注意,2.x 允许您在运行时定义模型而无需添加属性(尽管属性仍然是最方便的方法)

    【讨论】:

    • 噢!感谢您指向 byte=>stream 副本!我应该重新开始。或者使用你的。无论如何,更有趣的是,我注意到了一些有趣的文物。我将代码上传到 GIT 并在stackoverflow.com/questions/13735248/… 提出了一个新问题(因为它与这个问题相关但不同)。谢谢!!
    猜你喜欢
    • 2016-11-25
    • 2020-12-05
    • 2022-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多