web-dev-qa-db-ja.com

protobuf-netにはシリアル化用の圧縮機能が組み込まれていますか?

BinaryFormatterとprotobuf-netシリアライザーを比較していて、私が found であることに非常に満足していましたが、奇妙なことに、protobuf-netがオブジェクトをより小さなものにシリアル化できました。すべてのプロパティの値をメタデータなしでバイトの配列に書き込んだ場合よりもバイト配列。

AsReferencetrueに設定すると、protobuf-netが文字列インターンをサポートすることは知っていますが、この場合はサポートしていません。protobuf-netはデフォルトで圧縮を提供しますか?

自分で確認するために実行できるコードは次のとおりです。

var simpleObject = new SimpleObject
                       {
                           Id = 10,
                           Name = "Yan",
                           Address = "Planet Earth",
                           Scores = Enumerable.Range(1, 10).ToList()
                       };

using (var memStream = new MemoryStream())
{
    var binaryWriter = new BinaryWriter(memStream);
    // 4 bytes for int
    binaryWriter.Write(simpleObject.Id);      
    // 3 bytes + 1 more for string termination
    binaryWriter.Write(simpleObject.Name);    
    // 12  bytes + 1 more for string termination
    binaryWriter.Write(simpleObject.Address); 
    // 40 bytes for 10 ints
    simpleObject.Scores.ForEach(binaryWriter.Write); 

    // 61 bytes, which is what I expect
    Console.WriteLine("BinaryWriter wrote [{0}] bytes",
      memStream.ToArray().Count());
}

using (var memStream = new MemoryStream())
{
    ProtoBuf.Serializer.Serialize(memStream, simpleObject);

    // 41 bytes!
    Console.WriteLine("Protobuf serialize wrote [{0}] bytes",
      memStream.ToArray().Count());
}

編集:追加するのを忘れた、SimpleObjectクラスは次のようになります:

[Serializable]
[DataContract]
public class SimpleObject
{
    [DataMember(Order = 1)]
    public int Id { get; set; }

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

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

    [DataMember(Order = 4)]
    public List<int> Scores { get; set; }
}
26
theburningmonk

いいえ、違います; protobuf仕様で指定されているような「圧縮」はありません。ただし、(デフォルトでは)「varintencoding」を使用します。これは整数データの可変長エンコーディングであり、値が小さいほどスペースの使用量が少なくなります。したがって、0-127は1バイトとヘッダーを取ります。 varint自体は負の数に対してかなりループするため、小さいを許可する「ジグザグ」エンコーディングもサポートされていることに注意してください。マグニチュードの数値を小さくします(基本的に、正と負のペアをインターリーブします)。

実際、Scoresの場合は、「パックされた」エンコーディングも確認する必要があります。これには、[ProtoMember(4, IsPacked = true)]またはv2のTypeModelを介した同等のものが必要です(v2はどちらのアプローチもサポートします) 。これにより、単一のヘッダーとcombinedの長さを書き込むことにより、値ごとのヘッダーのオーバーヘッドが回避されます。 「パック」は、varint/zigzagで使用できます。値が大きくて予測できない可能性が高い知っているシナリオ用の固定長エンコーディングもあります。

注:ただし、データに大量のテキストが含まれている場合は、gzipまたはdeflateを使用してデータを追加で実行するとメリットが得られる場合があります。 しない場合、gzipとdeflateの両方で大きくなる可能性があります。

ワイヤーフォーマットの概要 ここにあります ;理解するのはそれほど難しいことではなく、さらに最適化するための最善の方法を計画するのに役立つ場合があります。

30
Marc Gravell