Protocol buffers 如何最大限度地发挥probuf网络的性能

Protocol buffers 如何最大限度地发挥probuf网络的性能,protocol-buffers,protobuf-net,Protocol Buffers,Protobuf Net,我想使用protobuf net序列化股票市场数据。我正在使用以下消息模型: 1st message: Meta Data describing what data to expect and some other info. 2nd message: DataBegin 3rd message: DataItem 4th message: DataItem ... nth message: EndData 下面是一个数据项的示例: class Bar{ DateTime DateTim

我想使用protobuf net序列化股票市场数据。我正在使用以下消息模型:

1st message: Meta Data describing what data to expect and some other info.
2nd message: DataBegin
3rd message: DataItem
4th message: DataItem
...
nth message: EndData
下面是一个数据项的示例:

class Bar{
   DateTime DateTime{get;set;}
   float Open{get;set}
   float High{get;set}
   float Low{get;set}
   float Close{get;set}
   intVolume{get;set}
 }
现在我正在使用TypeModel.SerializeWithLengthPrefix(…)来序列化每条消息(TypeModel已编译)。这很好,但比使用BinaryWriter手动序列化每条消息要慢10倍左右。这里重要的当然不是元数据,而是每个数据项的序列化。我拥有大量的数据,在某些情况下,数据是读/写到文件中的,因此性能至关重要

提高每个数据项的序列化和反序列化性能的好方法是什么


我应该直接在这里使用ProtoWriter吗?如果是,我该怎么做(我对协议缓冲区有点陌生)。

是的,如果您的数据是一组非常简单的同质记录,没有额外的要求(例如,它不需要转发兼容或版本优雅,也不需要从不完全了解所有数据的客户端可用),不需要方便地移植,您不介意手动实现所有序列化,那么是的:您可以更高效地手动实现。在快速测试中:

protobuf-net serialize: 55ms, 3581680 bytes
protobuf-net deserialize: 65ms, 100000 items
BinaryFormatter serialize: 443ms, 4200629 bytes
BinaryFormatter deserialize: 745ms, 100000 items
manual serialize: 26ms, 2800004 bytes
manual deserialize: 32ms, 100000 items
额外的空间可能是字段标记(如果手动打包记录,则不需要使用字段标记,也不需要担心同时使用的API的不同版本)

我当然不会复制“10倍”;我得到2倍,考虑到protobuf提供的东西,这还不错。而且肯定比二进制格式化程序要好得多,后者更像是20x!以下是一些功能:

  • 版本公差
  • 便携性
  • 模式使用
  • 无手动代码
  • 对子对象和集合的内置支持
  • 支持省略默认值
  • 支持常见的.NET场景(序列化回调;条件序列化模式等)
  • 继承(仅protobuf网络;不属于标准protobuf规范的一部分)
听起来,在您的场景中,手动序列化是要做的事情;没关系——我没有生气;p序列化库的目的是以不需要手动编写代码的方式解决更一般的问题

我的试验台:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ProtoBuf;
using ProtoBuf.Meta;
using System.Runtime.Serialization.Formatters.Binary;

public static class Program
{
    static void Main()
    {

        var model = RuntimeTypeModel.Create();
        model.Add(typeof(BarWrapper), true);
        model.Add(typeof(Bar), true);
        model.CompileInPlace();

        var data = CreateBar(100000).ToList();
        RunTest(model, data);

    }

    private static void RunTest(RuntimeTypeModel model, List<Bar> data)
    {
        using(var ms = new MemoryStream())
        {
            var watch = Stopwatch.StartNew();
            model.Serialize(ms, new BarWrapper {Bars = data});
            watch.Stop();
            Console.WriteLine("protobuf-net serialize: {0}ms, {1} bytes", watch.ElapsedMilliseconds, ms.Length);

            ms.Position = 0;
            watch = Stopwatch.StartNew();
            var bars = ((BarWrapper) model.Deserialize(ms, null, typeof (BarWrapper))).Bars;
            watch.Stop();
            Console.WriteLine("protobuf-net deserialize: {0}ms, {1} items", watch.ElapsedMilliseconds, bars.Count);
        }
        using (var ms = new MemoryStream())
        {
            var bf = new BinaryFormatter();
            var watch = Stopwatch.StartNew();
            bf.Serialize(ms, new BarWrapper { Bars = data });
            watch.Stop();
            Console.WriteLine("BinaryFormatter serialize: {0}ms, {1} bytes", watch.ElapsedMilliseconds, ms.Length);

            ms.Position = 0;
            watch = Stopwatch.StartNew();
            var bars = ((BarWrapper)bf.Deserialize(ms)).Bars;
            watch.Stop();
            Console.WriteLine("BinaryFormatter deserialize: {0}ms, {1} items", watch.ElapsedMilliseconds, bars.Count);
        }
        byte[] raw;
        using (var ms = new MemoryStream())
        {
            var watch = Stopwatch.StartNew();
            WriteBars(ms, data);
            watch.Stop();
            raw = ms.ToArray();
            Console.WriteLine("manual serialize: {0}ms, {1} bytes", watch.ElapsedMilliseconds, raw.Length);
        }
        using(var ms = new MemoryStream(raw))
        {
            var watch = Stopwatch.StartNew();
            var bars = ReadBars(ms);
            watch.Stop();
            Console.WriteLine("manual deserialize: {0}ms, {1} items", watch.ElapsedMilliseconds, bars.Count);            
        }

    }
    static IList<Bar> ReadBars(Stream stream)
    {
        using(var reader = new BinaryReader(stream))
        {
            int count = reader.ReadInt32();
            var bars = new List<Bar>(count);
            while(count-- > 0)
            {
                var bar = new Bar();
                bar.DateTime = DateTime.FromBinary(reader.ReadInt64());
                bar.Open = reader.ReadInt32();
                bar.High = reader.ReadInt32();
                bar.Low = reader.ReadInt32();
                bar.Close = reader.ReadInt32();
                bar.Volume = reader.ReadInt32();
                bars.Add(bar);
            }
            return bars;
        }
    }
    static void WriteBars(Stream stream, IList<Bar> bars )
    {
        using(var writer = new BinaryWriter(stream))
        {
            writer.Write(bars.Count);
            foreach (var bar in bars)
            {
                writer.Write(bar.DateTime.ToBinary());
                writer.Write(bar.Open);
                writer.Write(bar.High);
                writer.Write(bar.Low);
                writer.Write(bar.Close);
                writer.Write(bar.Volume);
            }
        }

    }
    static IEnumerable<Bar> CreateBar(int count)
    {
        var rand = new Random(12345);
        while(count-- > 0)
        {
            var bar = new Bar();
            bar.DateTime = new DateTime(
                rand.Next(2008,2011), rand.Next(1,13), rand.Next(1, 29),
                rand.Next(0,24), rand.Next(0,60), rand.Next(0,60));
            bar.Open = (float) rand.NextDouble();
            bar.High = (float)rand.NextDouble();
            bar.Low = (float)rand.NextDouble();
            bar.Close = (float)rand.NextDouble();
            bar.Volume = rand.Next(-50000, 50000);
            yield return bar;
        }
    }

}
[ProtoContract]
[Serializable] // just for BinaryFormatter test
public class BarWrapper
{
    [ProtoMember(1, DataFormat = DataFormat.Group)]
    public List<Bar> Bars { get; set; } 
}
[ProtoContract]
[Serializable] // just for BinaryFormatter test
public class Bar
{
    [ProtoMember(1)]
    public DateTime DateTime { get; set; }

    [ProtoMember(2)]
    public float Open { get; set; }

    [ProtoMember(3)]
    public float High { get; set; }

    [ProtoMember(4)]
    public float Low { get; set; }

    [ProtoMember(5)]
    public float Close { get; set; }

    // use zigzag if it can be -ve/+ve, or default if non-negative only
    [ProtoMember(6, DataFormat = DataFormat.ZigZag)]
    public int Volume { get; set; }
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.IO;
使用System.Linq;
使用ProtoBuf;
使用ProtoBuf.Meta;
使用System.Runtime.Serialization.Formatters.Binary;
公共静态类程序
{
静态void Main()
{
var model=RuntimeTypeModel.Create();
model.Add(typeof(BarWrapper),true);
添加(条的类型,真);
model.CompileInPlace();
var data=CreateBar(100000).ToList();
运行测试(模型、数据);
}
私有静态void运行测试(RuntimeTypeModel模型,列表数据)
{
使用(var ms=new MemoryStream())
{
var watch=Stopwatch.StartNew();
Serialize(ms,newbarwrapper{bar=data});
看,停;
WriteLine(“protobuf网络序列化:{0}ms,{1}字节”,watch.elapsedmillesons,ms.Length);
ms.Position=0;
watch=Stopwatch.StartNew();
var bar=((BarWrapper)model.Deserialize(ms,null,typeof(BarWrapper))).bar;
看,停;
WriteLine(“protobuf-net反序列化:{0}ms,{1}项”,watch.elapsedmillesons,bar.Count);
}
使用(var ms=new MemoryStream())
{
var bf=新的二进制格式化程序();
var watch=Stopwatch.StartNew();
序列化(ms,新的BarWrapper{bar=data});
看,停;
WriteLine(“二进制格式化程序序列化:{0}ms,{1}字节”,watch.elapsedmillesons,ms.Length);
ms.Position=0;
watch=Stopwatch.StartNew();
var bar=((BarWrapper)bf.反序列化(ms)).bar;
看,停;
WriteLine(“二进制格式化程序反序列化:{0}ms,{1}项”,watch.elapsedmillesons,bar.Count);
}
字节[]原始;
使用(var ms=new MemoryStream())
{
var watch=Stopwatch.StartNew();
写器(ms、数据);
看,停;
原始=ToArray女士();
WriteLine(“手动序列化:{0}ms,{1}字节”,watch.elapsedmillesons,raw.Length);
}
使用(var ms=新内存流(原始))
{
var watch=Stopwatch.StartNew();
var条=读取条(毫秒);
看,停;
WriteLine(“手动反序列化:{0}ms,{1}项”,watch.elapsedmillesons,bar.Count);
}
}
静态IList读条(流)
{
使用(变量读取器=新二进制读取器(流))
{
int count=reader.ReadInt32();
var条=新列表(计数);
同时(计数-->0)
{
var bar=新的bar();
bar.DateTime=DateTime.FromBinary(reader.ReadInt64());
bar.Open=reader.ReadInt32();
bar.High=reader.ReadInt32();
bar.Low=reader.ReadInt32();
bar.Close=reader.ReadInt32();
bar.Volume=reader.ReadInt32();
条。添加(条);
}
返回杆;
}
}
静态空写器(流、IList条)
{
使用(var writer=newbinarywriter(流))
{
writer.Write(bar.Count);
foreach(以条为单位的变量条)
{
writer.Write(bar.DateTime.ToBinary());
writer.Write(bar.Open);
writer.Write(bar.High);
writer.Write(bar.Low);
writer.Write(bar.Close);
作家,作家(酒吧)