Protobuf-net v2 и большие словари - PullRequest
1 голос
/ 06 декабря 2011

У меня происходит странная ситуация, которую я не совсем понимаю.

У меня есть класс «набор данных», который содержит различные метаданные о буе для мониторинга, включая список «датчиков».

Каждое текущее «сенсорное состояние».

Каждое «сенсорное состояние» содержит информацию о метаданных (временную метку, причину изменения и т. Д.), Но, что наиболее важно, имеет значения Dictionary<DateTime,float>.

Эти датчики обычно имеют более 50 тыс. Точек данных (за 15 минут считывания данных за годы), и поэтому я хотел найти что-то, что было бы немного быстрее при сериализации, чем стандартное .NET BinaryFormatter, и поэтому настроил Protobuf-net, который будет фантастически быстро сериализоваться.

К сожалению, моя проблема возникает при десериализации, когда мой словарь значений выдает исключение, поскольку там уже есть элемент с тем же добавленным ключом, и единственный способ получить его для десериализацииэто включить «OverwriteList», но я немного не уверен, почему, когда нет серийных ключей (это словарь), когда сериalizing, так почему появляются дубликаты ключей при десериализации?Это также вызывает проблемы с целостностью данных.

Любая помощь в объяснении этого будет принята с благодарностью.

(С другой стороны, при предоставлении идентификаторов атрибутов ProtoMember они должны быть уникальными длякласс или весь проект? и я ищу рекомендации по сжатию без потерь для использования вместе с protobuf-net, поскольку файлы становятся довольно большими)

Edit:

Я только что положилмой источник на GitHub и вот этот класс

SensorState (Примечание: в настоящее время он имеет OverwriteList = true, чтобы он работал для других разработок)

Вот пример файл необработанных данных

Я уже пытался использовать флаг SkipContructor, но даже если он установлен в значение true, он получает исключение, если OverwriteList также не имеет значения true для словаря значений.

Ответы [ 2 ]

2 голосов
/ 06 декабря 2011

Если OverwriteList исправляет это, то мне подсказывает *1003*, что в словаре есть некоторые данные по умолчанию, возможно, через конструктор или подобное. Если это действительно исходит от конструктора, вы можете отключить это с помощью [ProtoContract(SkipConstructor=true)].

Если я неправильно понял вышеизложенное, это может помочь проиллюстрировать воспроизводимый пример, если это возможно.

Что касается идентификаторов, они должны быть уникальными только внутри каждого типа, и рекомендуется сохранять их небольшими (из-за кодирования тегов «varint» маленькие ключи «дешевле», чем большие).

Если вы действительно хотите минимизировать размер, я бы посоветовал посмотреть и на содержимое данных. Например, вы говорите, что это 15-минутные чтения ... ну, я предполагаю, что есть случайные пробелы, но вы могли бы сделать, например:

Block (class)
    Start Time (DateTime)
    Values (float[])

и Block для каждой смежной связки 15-минутных значений (здесь предполагается, что каждое значение равно 15 после последнего, в противном случае запускается новый блок). Таким образом, вы храните несколько Block экземпляров вместо одного словаря. Это имеет преимущества:

  • намного меньше DateTime значений для хранения
  • вы можете использовать «упакованную» кодировку на поплавках, что означает, что не нужно добавлять все промежуточные теги; Вы делаете это, помечая массив / список как ([ProtoMember({key}, IsPacked = true)]) - отмечая, что он работает только с несколькими базовыми типами данных (не подобъектами)

в совокупности эти две настройки могут дать значительную экономию

Если в данных много строк, вы можете попробовать GZIP / DEFLATE. Конечно, вы можете попробовать в любом случае, но без большого количества строковых данных я бы с осторожностью ожидал слишком много дополнительного от сжатия.


В качестве обновления, основанного на прилагаемом файле данных (CSV), здесь нет внутренней проблемы при обработке словаря - как показано:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ProtoBuf;
class Program
{
    static void Main()
    {
        var data = new Data
        {
            Points =
            {
                {new DateTime(2009,09,1,0,0,0), 11.04F},
                {new DateTime(2009,09,1,0,15,0), 11.04F},
                {new DateTime(2009,09,1,0,30,0), 11.01F},
                {new DateTime(2009,09,1,0,45,0), 11.01F},
                {new DateTime(2009,09,1,1,0,0), 11F},
                {new DateTime(2009,09,1,1,15,0), 10.98F},
                {new DateTime(2009,09,1,1,30,0), 10.98F},
                {new DateTime(2009,09,1,1,45,0), 10.92F},
                {new DateTime(2009,09,1,2,00,0), 10.09F},
            }
        };

        var ms = new MemoryStream();
        Serializer.Serialize(ms, data);
        ms.Position = 0;
        var clone =Serializer.Deserialize<Data>(ms);
        Console.WriteLine("{0} points:", clone.Points.Count);
        foreach(var pair in clone.Points.OrderBy(x => x.Key))
        {
            float orig;
            data.Points.TryGetValue(pair.Key, out orig);
            Console.WriteLine("{0}: {1}", pair.Key, pair.Value == orig ? "correct" : "FAIL");
        }
    }
}
[ProtoContract]
class Data
{
    private readonly Dictionary<DateTime, float> points = new Dictionary<DateTime, float>();
    [ProtoMember(1)]
    public Dictionary<DateTime, float> Points { get { return points; } } 
}
1 голос
/ 07 декабря 2011

Здесь я извиняюсь за то, что предположил, что это как-то связано с кодом, который не был моим собственным делом.И хотя я здесь схожу с ума от команды Protobuf и Marc Gravell для protobuf-net, это очень быстро.

То, что происходило, происходило в классе Sensor , у меня была логика, чтобы никогдаПусть пара свойств никогда не будет нулевой.

[ProtoMember(12)]
public SensorState CurrentState
{
    get { return (_currentState == null) ? RawData : _currentState; }
    set { _currentState = value; }
}

Ссылка

[ProtoMember(16)]
public SensorState RawData
{
    get { return _rawData ?? (_rawData =  new SensorState(this, DateTime.Now, new Dictionary<DateTime, float>(), "", true, null)); }
    private set { _rawData = value; }
}

Ссылка

Пока это работаетфантастически, когда я использую свойства, это портит процессы сериализации.

Простое исправление заключалось в том, чтобы вместо этого пометить базовые объекты для сериализации.

[ProtoMember(16)]
private SensorState _rawData;
[ProtoMember(12)]
private SensorState _currentState;

Ссылка

...