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