即使对于简单实体,ProtoBuf 序列化也会丢失数据

2024-03-11

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

[更新#2]:我发现 ProtoBufs 仅在后续迭代中取得数量级领先。对于一次性序列化,BinaryFormatter 的速度要快一个数量级。为什么?单独提问...

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

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

Question

  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 Update 1、.NET4.5。顺便说一下,我使用以下方法得到了相同的结果[ProtoContract] and [ProtoMember(X)]习俗。文档不清楚,但它appearsDataContract 是较新的“统一”支持约定(对吗?)

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

首先,你的序列化/反序列化方法都被破坏了;你过度报告了结果(GetBuffer(), 没有Length),并且您没有将任何内容写入流中进行反序列化。这是一个正确的实现(尽管您也可以使用GetBuffer()如果你要回来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);
    }
}

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

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 倍。我宣布:获胜者。

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 并不是真正的“较新的‘统一’支持约定”——它当然不是“较新的”——我很确定它自从提交#4(可能更早)之类的东西以来就支持这两者。这只是为了方便而提供的选项:

  • 并非所有目标平台都有DataMemberAttribute
  • 有些人喜欢将 DTO 层限制为内置标记
  • 有些类型很大程度上超出了您的控制范围,但可能已经具有这些标记(例如,从 LINQ-to-SQL 生成的数据)
  • 另外,请注意,2.x 允许您在运行时定义模型,而无需添加属性(尽管属性仍然是最重要的)方便的方法来做到这一点)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

即使对于简单实体,ProtoBuf 序列化也会丢失数据 的相关文章

随机推荐