Оптимизация двоичной сериализации для многомерных универсальных массивов

StackOverflow https://stackoverflow.com/questions/223700

  •  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 в этом массиве и десериализаторе нужно знать это.

Но эти избыточные данные делают его хорошим кандидатом на сжатие, как отмечали другие.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top