Параллельная бинарная десериализация? - PullRequest
4 голосов
/ 20 декабря 2009

У меня есть решение, в котором мне нужно очень быстро считывать объекты в память, однако двоичный поток может кэшироваться в сжатой памяти, чтобы сэкономить время на диске.

Я возился с различными решениями, очевидно, что XmlTextWriter и XmlTextReader не были такими хорошими, как и встроенная двоичная сериализация. Protobuf-net превосходен, но все еще немного медленный. Вот некоторые характеристики:

Размер файла XML: 217 КБ

Двоичный размер файла: 87 КБ

Сжатый двоичный код: 26 КБ

Сжатый XML: 26 КБ

десериализация с XML (XmlTextReader): 8,4 сек

Десериализация с помощью двоичного кода (Protobuf-net): 6,2 сек.

Десериализация с помощью Binary wo string.interning (Protobuf-net): 5,2 сек

десериализация с двоичным из памяти: 5,9 сек

Время распаковки двоичного файла в память: 1,8 сек

Сериализация с Xml (XmlTextWriter): 11 сек

Сериализация с бинарным (Protobuf): 4 сек

Сериализация с префиксом двоичной длины (Protobuf-net): 3,8 сек.

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

Поскольку я немного новичок, когда дело доходит до бинарного ввода-вывода, я был бы признателен за некоторый вклад, прежде чем уделить время решению:)

Для простоты, скажем, мы хотим десериализовать список объектов без необязательного поля. Моей первой идеей было просто хранить каждый с префиксом длины. Прочитайте byte [] каждого в списке byte [] и используйте PLINQ для десериализации объекта byte [] ->.

Однако с помощью этого метода мне все еще нужно читать байты [] однопоточно, так что, возможно, вместо этого можно было бы прочитать весь двоичный поток в память (насколько большие двоичные файлы возможны для этого кстати?) И в начале двоичного файла вместо этого сохраните количество объектов, а также их длину и смещение. Тогда я смогу просто создать ArraySegments или что-то еще и выполнить разбивку на параллели.

Так что вы думаете, ребята, возможно ли это?

Ответы [ 4 ]

2 голосов
/ 23 декабря 2009

Я делаю такие вещи довольно часто, и ничто не сравнится с использованием BinaryReader для чтения. Насколько я знаю, нет более быстрого способа, чем использование BinaryReader.ReadInt32 для чтения в 32-разрядном целом числе.

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

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

1 голос
/ 22 декабря 2009

Двоичный файл может быть прочитан одновременно несколькими потоками. Для этого он должен быть открыт с соответствующими модификаторами доступа / общего доступа. И тогда каждый поток может получить свое собственное смещение и длину в этом файле. Таким образом, параллельное чтение не является проблемой.

Предположим, что вы будете придерживаться простого двоичного формата: каждый объект имеет префикс своей длины. Зная, что вы можете «прокрутить» файл и узнать смещение, куда поместить поток десериализации.

десериализация алгоритма может выглядеть так: 1) проанализировать файл (разделить его на несколько относительно больших кусков, граница фрагмента должна совпадать с границей объекта) 2) создать необходимое количество потоков десериализатора и «проинструктировать» их с соответствующим смещением и длиной для чтения 3) объединить результаты всех потоков десериализатора в один список

0 голосов
/ 23 декабря 2009

Это заставило меня задуматься, кажется (поправьте меня, если я ошибаюсь), что Основным виновником десериализации является фактическое преобразование байтов, а не IO.

Не думайте, где время тратится, найдите себе профилировщик и узнайте.

0 голосов
/ 20 декабря 2009

Когда я десериализирую список объектов размером более 1 МБ xml, я десериализую менее 2 секунд с этим кодом:

public static List<T> FromXML<T>(this string s) where T : class
        {
            var ls = new List<T>();
            var xml = new XmlSerializer(typeof(List<T>));
            var sr = new StringReader(s);
            var xmltxt = new XmlTextReader(sr);
            if (xml.CanDeserialize(xmltxt))
            {
                ls = (List<T>)xml.Deserialize(xmltxt);
            }
            return ls;
        }

Попробуйте это, если лучше для случая XML?

...