2

[更新#1] :ベンチマークをチェックすることに興味がある人がいれば、修正および修正した「デモ」プロジェクトをhttps://github.com/sidshetye/SerializersCompareにアップロードしました。

[更新#2]:ProtoBufsは、後続の反復でのみ桁違いにリードしていることがわかります。1回限りのシリアル化の場合、BinaryFormatterは1桁高速です。なんで?別の質問..。

BinaryFormatter、Json.NET、およびProtoBuf.NETを比較しようとしています(後者は今日NuGetから取得しました)。ProtoBufは実際のフィールドを出力せず、すべてnullと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 Update 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";
    }
}

ここに興味がある人のために、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

まず、シリアル化/逆シリアル化のメソッドが両方とも壊れています。結果を過大報告しており(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分の1でした。私は宣言します:勝者。

コード:

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(...)]サポートは実際には「より新しい」「均一な」サポート規則ではありません-確かに「より新しい」ではありません-コミット#4(そしておそらくそれ以前)のようなもの以来、両方をサポートしていると確信しています。これは、便宜上提供されているオプションにすぎません。

  • すべてのターゲットプラットフォームが持っているわけではありませんDataMemberAttribute
  • DTOレイヤーを組み込みのマーカーに制限することを好む人もいます
  • 一部のタイプは主に制御できませんが、すでにそれらのマーカーが含まれている場合があります(たとえば、LINQ-to-SQLから生成されたデータ)
  • さらに、2.xを使用すると、属性を追加しなくても実行時にモデルを定義できることに注意してください(ただし、属性は依然として最も便利な方法です)。
于 2012-12-05T07:49:19.377 に答える