Оптимизация двоичной сериализации для многомерных универсальных массивов
-
03-07-2019 - |
Вопрос
У меня есть класс, который мне нужен для двоичной сериализации. Класс содержит одно поле, как показано ниже:
private T[,] m_data;
Эти многомерные массивы могут быть довольно большими (сотни тысяч элементов) и любого примитивного типа. Когда я попробовал стандартную сериализацию .net на объекте, файл, записанный на диск, был большим, и я думаю, что .net хранит много повторяющихся данных о типах элементов и, возможно, не так эффективно, как можно было бы сделать.
Я искал пользовательские сериализаторы, но не видел ни одного, имеющего дело с многомерными универсальными массивами. Я также экспериментировал со встроенным сжатием .net в байтовом массиве потока памяти после сериализации с некоторым успехом, но не так быстро / сжато, как я надеялся.
Мой вопрос заключается в том, стоит ли мне пытаться написать собственный сериализатор, чтобы оптимально сериализовать этот массив для соответствующего типа (это кажется немного утомительным), или мне следует использовать стандартную сериализацию .net и добавить сжатие?
Любой совет по наилучшему подходу был бы наиболее ценным, или ссылки на ресурсы, показывающие, как заняться сериализацией многомерного универсального массива - как упомянуто существующие примеры , которые я обнаружил, не поддерживают такие структуры.
Решение
Вот что я придумала. Приведенный ниже код создает int [1000] [10000] и записывает его с помощью BinaryFormatter в 2 файла - один в архиве, а другой - нет. Р>
Размер сжатого файла составляет 1,19 МБ (1 255 339 байт). Распакованный размер - 38,2 МБ (40 150 034 байта).
int width = 1000;
int height = 10000;
List<int[]> list = new List<int[]>();
for (int i = 0; i < height; i++)
{
list.Add(Enumerable.Range(0, width).ToArray());
}
int[][] bazillionInts = list.ToArray();
using (FileStream fsZ = new FileStream("c:\\temp_zipped.txt", FileMode.Create))
using (FileStream fs = new FileStream("c:\\temp_notZipped.txt", FileMode.Create))
using (GZipStream gz = new GZipStream(fsZ, CompressionMode.Compress))
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(gz, bazillionInts);
f.Serialize(fs, bazillionInts);
}
Я не могу придумать лучший / простой способ сделать это. Молниеносная версия чертовски тугая.
Я бы пошел с BinaryFormatter + GZipStream. Делать что-то нестандартное было бы совсем не весело.
<Ч>[отредактировано MG] Я надеюсь, что вы не будете обижены редактированием, но равномерный повторный диапазон (0, ширина) сильно искажает; изменить на:
int width = 1000;
int height = 10000;
Random rand = new Random(123456);
int[,] bazillionInts = new int[width, height];
for(int i = 0 ; i < width;i++)
for (int j = 0; j < height; j++)
{
bazillionInts[i, j] = rand.Next(50000);
}
И попробуй это; вы увидите temp_notZipped.txt
на 40 МБ, temp_zipped.txt
на 62 МБ. Не очень привлекательно ...
Другие советы
Наилучшим соотношением длины кода к размеру вывода будет кодирование массива с использованием BitConverter, преобразование всех элементов в их компактный двоичный формат. Это руководство, я знаю, но сэкономит 80-90% места по сравнению с двоичной сериализацией .NET.
Можете ли вы определить " большой " ;? Пример 1000x10000xint (другой пост) выходит на 40Mb; и 1000x10000x4 байта (= int) составляет 38 МБ. Что касается накладных расходов, это не страшно.
Какими данными T может быть? Просто приматы? Я думаю, что я мог бы отредактировать protobuf-net для поддержки прямоугольных массивов < code> * - но для обеспечения некоторой совместимости проводов нам, вероятно, понадобится заголовок (один байт) на элемент - т. е. 9 МБ служебных данных для примера 1000x10000.
Это, вероятно, не стоит для таких вещей, как float
, double
и т. д. (поскольку они хранятся дословно в «буферах протокола»), но могут быть экономия на таких вещах, как int
, просто из-за того, как он упаковывает целые числа ... (особенно если они имеют тенденцию быть на меньшую сторону [величина]). Наконец, если T на самом деле является объектами типа Person
и т. Д., То он должен быть на много лучше, чем двоичная сериализация, поскольку он очень хорош для упаковки объектов.
Это не было бы тривиально, чтобы рожок для обуви в прямоугольных массивах, но дайте мне знать, если это то, что вы хотели бы попробовать.
*
: в данный момент этого не происходит, поскольку " буферы протокола " spec не поддерживает их, но мы можем взломать это ...
Причина, по которой должно быть так много данных о типах, состоит в том, что ваш массив T может быть любого типа, но более конкретно, T может иметь тип SomeBaseClass, и вы все равно можете хранить SomeDerivedClass в этом массиве и десериализаторе нужно знать это.
Но эти избыточные данные делают его хорошим кандидатом на сжатие, как отмечали другие.