30

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

AsReferenceに設定すると、 protobuf-net が文字列のインターンをサポートすることは知っていますtrueが、この場合はそうしていないので、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; }
}
4

2 に答える 2

38

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

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

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

ワイヤー形式の概要はこちらです。理解するのはそれほど難しくなく、さらに最適化する最善の方法を計画するのに役立つ場合があります。

于 2011-08-24T20:32:42.563 に答える
0

少なくとも、C++ ライブラリは圧縮ストリームへの書き込みと圧縮ストリームからの書き込みをサポートしています。

https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/io/gzip_stream.h

それが.Net実装に移植されているかどうかはわかりません。

于 2020-09-12T00:12:35.730 に答える