【问题标题】:How to write fixed length records in Protbuf-net?如何在 Protobuf-net 中写入固定长度的记录?
【发布时间】:2014-04-05 09:58:38
【问题描述】:

这是我使用 Protobuf-net 进行序列化的代码的主要部分。我有大量记录要循环并写入文件。

我现在想将所有记录设为 FIXED SIZE,以便稍后在反序列化中我可以一次跳过几条记录。

如何修改此代码以写入 FIXED LENGTH 记录?

       List<SP> SortedData = Data.OrderBy(o => o.DT).ToList();

        string LastdatFileName = "";
        FileStream outBin = null;

        foreach (var d in SortedData)
        {
            string binFileName = "n" + symbol + d.DT.ToString("yyyyMMdd") + ".dat";

            if (!datFileName.Equals(LastdatFileName))
            {
                if (outBin != null)
                {
                    outBin.Close();
                }

                outBin = File.Create(dbDirectory + @"\" + binFileName, 2048, FileOptions.None);
                LastdatFileName = datFileName;
            }

            Serializer.SerializeWithLengthPrefix(outBin, d.ToTickRecord(),PrefixStyle.Base128);

        }

        outBin.Close();

记录

  [ProtoContract]
    public class TickRecord
    {
        [ProtoMember(1)]
        public DateTime DT;
        [ProtoMember(2)]
        public double BidPrice;
        [ProtoMember(3)]
        public double AskPrice;
        [ProtoMember(4)]
        public int BidSize;
        [ProtoMember(5)]
        public int AskSize;

        public TickRecord(DateTime DT, double BidPrice, double AskPrice, int BidSize, int AskSize)
        {
            this.DT = DT;
            this.BidPrice = BidPrice;
            this.AskPrice = AskPrice;
            this.BidSize = BidSize;
            this.AskSize = AskSize;

        }
}

反序列化

             long skipRate = 10;


                    while ((tr = Serializer.DeserializeWithLengthPrefix<TickRecord>(fs, PrefixStyle.Base128)) != null) //fs.Length > fs.Position)
                    {

                        count++;

                        fs.Position += (38 * skipRate);
                        if (fs.Position > fs.Length)
                            break;

                        //Console.WriteLine("> " + tr.ToString());

                    }

Marc Gravell 的 SSCCE

您将需要创建 2 个按钮序列化和反序列化。

序列化创建一个虚拟数据文件。

反序列化读取它。

注释掉 fs.Position 行以查看整个文件的原始读取。在我的机器上需要 12 秒。 然后取消注释,文件将每次跳过 10 条记录。希望速度提高 10 倍,但在我的机器上需要 8 秒。所以我认为改变 fs.Position 很昂贵。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ProtoBuf;
using System.IO;
using System.Diagnostics;

namespace BinTest3
{


    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Serialize_Click(object sender, EventArgs e)
        {

            FileStream outBin = null;

            string binFileName = @"C:\binfile.dft";
            outBin = File.Create(binFileName, 2048, FileOptions.None);

            DateTime d = DateTime.Now;

            TickRecord tr = new TickRecord(d, 1.02, 1.03,200,300);

            for (int i =0; i < 20000000; i++)
            {
                tr.BidPrice += 1;
                Serializer.SerializeWithLengthPrefix(outBin, tr, PrefixStyle.Base128);
            }

            outBin.Close();
            label1.Text = "Done ";
        }

        private void Deserialize_Click(object sender, EventArgs e)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            FileStream fs;
            string binFileName = @"C:\binfile.dft";

            fs = new FileStream(binFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4 * 4096);
            long skipRate =10;
            int count = 0;
            TickRecord tr;

            long skip = (38*skipRate);
            try
            {
                while ((tr = Serializer.DeserializeWithLengthPrefix<TickRecord>(fs, PrefixStyle.Base128)) != null) //fs.Length > fs.Position)
                {
                    count++;

                    fs.Position += skip;  //Comment out this line to see raw speed

                }
            }
            catch (Exception)
            {

            }

            fs.Close();

            sw.Stop();
            label1.Text = "Time taken: " + sw.Elapsed + " Count: " + count.ToString("n0");

        }
    }


    [ProtoContract]
    public class TickRecord
    {

        [ProtoMember(1, DataFormat = DataFormat.FixedSize)]
        public DateTime DT;
        [ProtoMember(2)]
        public double BidPrice;
        [ProtoMember(3)]
        public double AskPrice;
        [ProtoMember(4, DataFormat = DataFormat.FixedSize)]
        public int BidSize;
        [ProtoMember(5, DataFormat = DataFormat.FixedSize)]
        public int AskSize;

        public TickRecord()
        {

        }

        public TickRecord(DateTime DT, double BidPrice, double AskPrice, int BidSize, int AskSize)
        {
            this.DT = DT;
            this.BidPrice = BidPrice;
            this.AskPrice = AskPrice;
            this.BidSize = BidSize;
            this.AskSize = AskSize;

        }



    }
}

【问题讨论】:

  • 协议缓冲区有线格式只是不“做”固定长度的记录。您可能一次写入 N 条记录块(作为单独的消息) - 因为有线格式使用长度前缀,这可以让您非常快速地跳过这些 N 条记录(至少在理论上不解析任何其他内容)。
  • @JonSkeet 我的目标是在反序列化上跳过不同数量的记录。所以不要在序列化上指定阻塞会很方便。
  • 正如我所说,Protocol Buffers 有线格式并不是这样设计的。我能想到的最接近的方法是使用带有一些“填充”字段的消息格式,您必须为每条消息仔细构建这些字段以使其大小合适。不过这会很繁琐。
  • 我不介意序列化方面的繁琐。而且过程可能很慢。但我需要快速反序列化。有什么想法可以添加“填充”字段吗?
  • 我会添加一个字节字段,并研究有线格式(记录在主 protobuf 项目站点中)以计算出标签将占用多少字节。根据您需要的填充范围,当添加一个 mire 字节也会扩展标签时,您可能需要两个字段来处理。

标签: c# serialization protobuf-net fixed-length-record


【解决方案1】:

快速浏览文档后,我认为您需要以下内容:

[ProtoMember(1, DataFormat = DataFormat.FixedSize)]
public DateTime DT;
[ProtoMember(2,)]
public double BidPrice;
[ProtoMember(3)]
public double AskPrice;
[ProtoMember(4, DataFormat = DataFormat.FixedSize)]
public int BidSize;
[ProtoMember(5, DataFormat = DataFormat.FixedSize)]
public int AskSize;

这对于数值应该没问题 - 我不确定DataFormat 属性是否适用于DateTime 字段。另一种方法是使用long Ticks,它使用FixedSize 数据格式进行序列化,然后是一个与DateTime 相互转换的属性。不过,看看代码,我认为它会像上面写的那样没问题。无需指定 double 的数据格式,因为无论如何它总是写为固定大小的值。

【讨论】:

  • 了解乔恩。我之前确实看过那个页面——但对我来说,它的用途并不明显。我仍在尝试查找更多 Protobuf-net 文档。但是,您的解决方案似乎有效 - 非常感谢
  • @MarcGravell 这很奇怪。请参阅我上面显示反序列化代码的编辑。即使我每次都跳过 10 条记录(我的结构大小是 38)。 (200 条 mio 记录)所用时间从 2 分钟缩短到 1.45 分钟!你认为这是因为改变 fs.Postion 很昂贵吗?
  • @Man 很难说没有具体的例子
  • @MarcGravell 请参阅问题的编辑。我添加了一个具体的例子
猜你喜欢
  • 2016-09-01
  • 2020-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-14
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多