Сериализация и десериализация VLarge Dictionary в С#

У нас есть v.large Dictionary<long,uint> (несколько миллионов записей) как часть высокопроизводительного приложения С#. Когда приложение закрывается, мы сериализуем словарь на диск с помощью BinaryFormatter и MemoryStream.ToArray(). Сериализация возвращается примерно через 30 секунд и создает файл размером около 200 МБ. Когда мы затем попытаемся десериализировать словарь, используя следующий код:

BinaryFormatter bin = new BinaryFormatter();
Stream stream = File.Open("filePathName", FileMode.Open);
Dictionary<long, uint> allPreviousResults =
    (Dictionary<long, uint>)bin.Deserialize(stream);
stream.Close();

Это займет около 15 минут. Мы пробовали альтернативы, и медленная часть определенно bin.Derserialize(stream), то есть байты считываются с жесткого диска (высокопроизводительный SSD) менее чем за 1 секунду.

Кто-нибудь может указать, что мы делаем неправильно, так как мы хотим, чтобы время загрузки было в том же порядке, что и время сохранения.

С уважением, Марк

Ответ 1

Вы можете проверить protobuf-net или просто сериализовать его самостоятельно, что, вероятно, будет самым быстрым, что вы можете получить.

class Program
{
    public static void Main()
    {
        var dico = new Dictionary<long, uint>();
        for (long i = 0; i < 7500000; i++)
        {
            dico.Add(i, (uint)i);
        }

        using (var stream = File.OpenWrite("data.dat"))
        using (var writer = new BinaryWriter(stream))
        {
            foreach (var key in dico.Keys)
            {
                writer.Write(key);
                writer.Write(dico[key]);
            }
        }

        dico.Clear();
        using (var stream = File.OpenRead("data.dat"))
        using (var reader = new BinaryReader(stream))
        {
            while (stream.Position < stream.Length)
            {
                var key = reader.ReadInt64();
                var value = reader.ReadUInt32();
                dico.Add(key, value);
            }
        }
    }
}

размер результирующего файла = > 90 Мбайт (85.8 МБ).

Ответ 2

Просто чтобы показать подобную сериализацию (к принятому ответу) через protobuf-net:

using System.Collections.Generic;
using ProtoBuf;
using System.IO;

[ProtoContract]
class Test
{
    [ProtoMember(1)]
    public Dictionary<long, uint> Data {get;set;}
}

class Program
{
    public static void Main()
    {
        Serializer.PrepareSerializer<Test>();
        var dico = new Dictionary<long, uint>();
        for (long i = 0; i < 7500000; i++)
        {
            dico.Add(i, (uint)i);
        }
        var data = new Test { Data = dico };
        using (var stream = File.OpenWrite("data.dat"))
        {
            Serializer.Serialize(stream, data);
        }
        dico.Clear();
        using (var stream = File.OpenRead("data.dat"))
        {
            Serializer.Merge<Test>(stream, data);
        }
    }
}

Размер: 83 мг - но самое главное, вам не пришлось делать все это вручную, вводя ошибки. Fast тоже (будет еще быстрее в "v2" ).

Ответ 3

Возможно, вы захотите использовать профилировщик, чтобы увидеть, если за кулисами десериализатор выполняет кучу отражения на лету.

Пока, если вы не хотите использовать базу данных, попробуйте сохранить ваши объекты в виде файла flatfile в настраиваемом формате. Например, в первой строке файл дает общее количество записей в словаре, что позволяет вам создать словарь с заданным размером. Остальные строки будут представлять собой ряд пар ключ-значение с фиксированной шириной, представляющих все записи в словаре.

С вашим новым файловым форматом используйте StreamReader для чтения в вашем файле по очереди или в фиксированных блоках, посмотрите, позволяет ли это быстрее читать ваш словарь.

Ответ 4

Есть несколько быстрых решений NoSQL для Key-Value, почему бы не попробовать их? В качестве примера ESENT кто-то разместил его здесь в SO. managedesent