Сериализация и десериализация 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