Как писать записи фиксированной длины в Protbuf-net?

Вот основная часть моего кода для сериализации с использованием Protobuf-net. У меня есть очень большое количество записей, которые я перебираю и записываю в файл.

Теперь я хочу сделать все записи ФИКСИРОВАННЫМ РАЗМЕРОМ, чтобы позже в десериализации я мог пропустить несколько записей одновременно.

Как мне изменить этот код для записи записей с фиксированной длиной?

       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;

        }
}

Deserialize

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

                    }

SSCCE для Марка Гравелла

Вам нужно будет создать 2 кнопки Serialize и Deserialize.

Serialize создает фиктивный файл данных.

Десериализация читает через это.

Закомментируйте строку 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;

        }



    }
}

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 в любом случае это всегда записывается как значение фиксированного размера.

Другие вопросы по тегам