Оптимизация двоичной сериализации для многомерных универсальных массивов - PullRequest
7 голосов
/ 22 октября 2008

У меня есть класс, который мне нужен для двоичной сериализации. Класс содержит одно поле, как показано ниже:

private T[,] m_data;

Эти многомерные массивы могут быть довольно большими (сотни тысяч элементов) и любого примитивного типа. Когда я попробовал стандартную сериализацию .net для объекта, файл, записанный на диск, был большим, и я думаю, что .net хранит много повторяющихся данных о типах элементов и, возможно, не так эффективно, как это могло бы быть сделано.

Я искал нестандартные сериализаторы, но не видел ни одного, имеющего дело с многомерными универсальными массивами. Я также экспериментировал со встроенным сжатием .net в массиве байтов потока памяти после сериализации с некоторым успехом, но не так быстро / сжато, как я надеялся.

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

Любой совет по наилучшему подходу был бы наиболее ценным, или ссылки на ресурсы, показывающие, как решить сериализацию многомерного универсального массива - как уже упоминалось существующие примеры Я обнаружил, что не поддерживают такие структуры.

Ответы [ 4 ]

5 голосов
/ 22 октября 2008

Вот что я придумал. Приведенный ниже код создает 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 МБ. Не так привлекательно ...

0 голосов
/ 25 сентября 2009

Причина, по которой должно быть так много данных о типах, состоит в том, что ваш массив T может быть любого типа, но более конкретно, T может иметь тип SomeBaseClass, и вы все равно можете хранить SomeDerivedClass в этом массиве и десериализаторе нужно знать это.

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

0 голосов
/ 22 октября 2008

Можете ли вы определить "большой"? Пример 1000x10000xint (другой пост) выходит на 40Mb; и 1000x10000x4 байта (= int) составляет 38 МБ. Что касается накладных расходов, это не страшно.

Какими данными T, вероятно, будет? Просто приматы? Я думаю, что я мог бы, вероятно, отредактировать protobuf-net для поддержки прямоугольных массивов * - но для сохранения некоторой совместимости проводов нам, вероятно, потребуется заголовок (один байт) на элемент - т.е. 9 МБ служебных данных для примера 1000x10000.

Это, вероятно, не стоит для таких вещей, как float, double и т. Д. (Поскольку они хранятся дословно в «буферах протокола») - но для таких вещей, как int, может быть экономия просто из-за того, как он упаковывает целые числа ... (особенно если они имеют тенденцию быть на меньшей стороне [величина]). Наконец, если T на самом деле объекты типа Person и т. Д., То он должен быть lot лучше, чем двоичная сериализация, поскольку он очень хорош в упаковке объектов.

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

*: на данный момент этого не происходит, поскольку спецификация «протокольные буферы» их не поддерживает, но мы можем взломать это ...

0 голосов
/ 22 октября 2008

Лучшим соотношением длины кода к размеру вывода будет кодирование массива с использованием BitConverter, преобразование всех элементов в их компактный двоичный формат. Это руководство, я знаю, но сэкономит 80-90% места по сравнению с двоичной сериализацией .NET.

...