Быстрое чтение / запись сложных графов объектов .Net - PullRequest
4 голосов
/ 04 июня 2009

У меня есть собственная структура данных, написанная на C # (структура довольно сложная). Мне нужно сериализовать и десериализовать структуру. Размер сериализованного файла на диске иногда может быть довольно большим (около 1 ГБ), но также может быть и небольшим (в зависимости от количества сохраненных записей). У меня есть следующие требования:

  1. Сериализация и десериализация должны быть очень быстрыми
  2. Я должен быть в состоянии частично десериализовать большой файл (то есть получить доступ только к некоторым соответствующим записям), потому что, если я десериализую весь файл с диска, использование памяти будет слишком высоким.
  3. Должен быть потокобезопасным, так как несколько процессов могут записывать / читать записи из файла

Я знаю, это звучит так, будто мне нужна база данных, но я не могу использовать ее по нескольким причинам. Я попытался выполнить требование 1, внедрив ISerializable, что сделало его намного быстрее, чем использование .net, встроенного в сериализаторы Binary / XML, но не достаточно быстро. Для требования 2 полностью ставит меня в тупик.

У кого-нибудь есть идеи, как это сделать? Я думаю, что любой, кому приходилось сохранять собственные большие форматы файлов, сталкивался с подобными проблемами.

С уважением, Сэм

Ответы [ 4 ]

2 голосов
/ 04 июня 2009

Является ли дерево данных или полный график - т.е. есть ли циклические ссылки? Если нет, protobuf-net - это высокопроизводительный двоичный сериализатор tree . Он поддерживает потоковую передачу перечислимых элементов (так что вы можете пропускать записи и т. Д., А не буферизировать все), но для эффективного поиска случайного элемента, я ожидаю, что вам потребуется какой-то индекс.

Чтение / запись ОЧЕНЬ сложно для одного файла; в частности, запись может потребовать перемещения большего количества диска, чем вы ожидаете ... чтение также сложно, и может потребоваться синхронизация. Было бы проще использовать отдельные файлы ...


Пример пропуска ранних предметов; Возможно, я мог бы добавить вспомогательный метод, но метод TryDeserializeWithLengthPrefix будет работать ... ключевой момент заключается в том, чтобы наблюдать, что между сериализацией и десериализацией мы создаем только один дополнительный объект.

using System;
using System.IO;
using System.Threading;
using ProtoBuf;

[ProtoContract]
class Foo {
    static int count;
    public static int ObjectCount { get { return count; } }
    public Foo() { // track how many objects have been created...
        Interlocked.Increment(ref count);
    }
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public double Bar { get; set; }    
}
static class Program {
    static void Main() {
        MemoryStream ms = new MemoryStream();
        Random rand = new Random();
        for (int i = 1; i <= 5000; i++) {
            Foo foo = new Foo { Bar = rand.NextDouble(), Id = i };
            Serializer.SerializeWithLengthPrefix(ms, foo,PrefixStyle.Base128, 1);
        }
        ms.Position = 0;
        // skip 1000
        int index = 0;
        object obj;
        Console.WriteLine(Foo.ObjectCount);
        Serializer.NonGeneric.TryDeserializeWithLengthPrefix(
            ms, PrefixStyle.Base128,
            tag => ++index == 1000 ? typeof(Foo) : null, out obj);
        Console.WriteLine(Foo.ObjectCount);
        Console.WriteLine(((Foo)obj).Id);
    }
}
2 голосов
/ 04 июня 2009

Я не работал ни по одному сценарию, как у вас здесь. Тем не менее, в прошлом я обсуждал аналогичную проблему, и вот результат обсуждения. (Хотя, признаюсь, я никогда не видел реализацию). Кроме того, я боюсь, что не может быть никакого простого прямого решения.

Предположения:

я. Данные для записи сортируются.

Решение:

я. Фрагмент вашего хранилища данных в несколько файлов. Выделите диапазон отсортированных значений для каждого файла. например. запись 1-10000 в файле 1, запись 100001-20000 в файле 2 и т. д.

II. Когда вы пишете / читаете данные, вы знаете диапазон заранее, чтобы вы могли соответствовать пункту 2.

III. Это также решит пункт 3, если вероятность того, что два или более процесса запросят одинаковые данные, будет меньше.

Чтобы предоставить более точное решение, нам нужно больше информации о том, чего вы пытаетесь достичь.

0 голосов
/ 04 июня 2009

Для частичной (или разделенной) десериализации (которую я сам искал, например, динамические и статические части на игровом уровне), я думаю, вам придется написать свой собственный движок сериализации.

0 голосов
/ 04 июня 2009

Я думаю, нам понадобится больше информации о том, как на самом деле выглядит файл ...

Разве вы не можете просто прочитать кусочки sizeof (yourstruct) из файла и обработать их отдельно, считывая все записи в памяти?

...