2011-08-24 13 views
18

Estaba haciendo una comparación entre BinaryFormatter y serializador Protobuf-net y estaba bastante contento con lo que found, pero lo que es extraño es que Protobuf-net logró serializar los objetos en una matriz de bytes más pequeños de lo que obtendría si solo escribió el valor de cada propiedad en una matriz de bytes sin ningún metadato.¿Tiene Protobuf-net compresión incorporada para la serialización?

Sé que Protobuf-net admite la intercepción de cadenas si establece AsReference en true, pero no estoy haciendo eso en este caso, ¿así que Protobuf-net proporciona cierta compresión por defecto?

Aquí hay un código que puede ejecutar para ver por sí mismo:

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

EDIT: se olvidó de agregar, la clase SimpleObject se ve así:

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

Respuesta

26

No, no lo hace; no hay "compresión" como tal especificada en la especificación protobuf; sin embargo, utiliza (de forma predeterminada) "codificación varint": una codificación de longitud variable para datos enteros que significa que los valores pequeños usan menos espacio; entonces 0-127 toma 1 byte más el encabezado. Tenga en cuenta que varint por sí mismo se vuelve bastante repetitivo para los números negativos, por lo que también se admite la codificación "zigzag" que permite que los números de magnitud sean pequeños (básicamente, intercala pares positivos y negativos).

En realidad, en su caso para Scores también se debe buscar en la codificación "lleno", lo que requiere ya sea [ProtoMember(4, IsPacked = true)] o el equivalente a través de TypeModel en la versión 2 (v2 es compatible con cualquiera de los enfoques). Esto evita la sobrecarga de un encabezado por valor, escribiendo un solo encabezado y la longitud combinada. "Packed" se puede usar con varint/zigzag. También hay codificaciones de longitud fija para escenarios en los que sabe, los valores son probablemente grandes e impredecibles.

Tenga en cuenta también: pero si sus datos tienen mucho texto, puede beneficiarse de ejecutarlo a través de gzip o desinflar; si no es, entonces tanto gzip como desinfle podrían hacer que crezca.

Información general sobre el formato de cable is here; no es muy difícil de entender y puede ayudarlo a planificar la mejor manera de optimizar aún más.

+0

gracias, ¡todo tiene sentido ahora! – theburningmonk

+0

¿Por qué protobuf hace 1 byte solo para los 128 valores? 8 bits permite escribir 256 valores diferentes. – tobi

+6

@tobi usa la codificación "varint" para el número de campo, lo que significa 7 bits de carga útil, y 1 bit "hay otro byte para leer". Sigues leyendo hasta que el MSB sea cero. –

Cuestiones relacionadas