Параллельная двоичная десериализация?

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

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

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

Размер файла в двоичном формате: 87 kb

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

Сжатый XML: 26 КБ

Десериализация с помощью XML (XmlTextReader): 8,4 сек.

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

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

Десериализация с помощью двоичного файла из памяти: 5,9 сек.

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

Сериализация с помощью Xml (XmlTextWriter): 11 сек.

Сериализация с двоичным кодом (Protobuf): 4 сек.

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

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

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

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

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

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


person Homde    schedule 20.12.2009    source источник
comment
Извините, на некоторые вопросы я действительно не получил однозначного ответа, но вначале я просто не понимал, что нужно его задать. Урок усвоен, и спасибо, что указали на него. Кажется, вы не можете дать ответ на старые вопросы, или я что-то упустил?   -  person Homde    schedule 20.12.2009
comment
В поддержку Алона этот вопрос сравнения строк Java, который вы опубликовали, имел несколько замечательно продуманных ответов ...   -  person Jason D    schedule 20.12.2009
comment
Согласен, как мне задать правильный ответ?   -  person Homde    schedule 20.12.2009
comment
Да ладно, я тупой, не понимал, что можно щелкнуть символ v :)   -  person Homde    schedule 20.12.2009
comment
Сек секунды? Если так, похоже, что где-то что-то ужасно не так. Мы десериализуем документы размером в десятки МБ за доли секунды ...   -  person nitzmahone    schedule 20.12.2009


Ответы (4)


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

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

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

person Nick Randell    schedule 23.12.2009

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

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

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

person Vadym Stetsiak    schedule 22.12.2009
comment
Привет, я работаю над подобным решением, но для параллельного чтения с диска я решил сначала получить весь файл в байтовый буфер, а затем десериализовать / сериализовать его. Кажется, намного быстрее, если вы читаете с диска параллельно, вы будете ограничены скоростью диска. Я опубликую здесь некоторую информацию о своем решении, когда закончу, спасибо - person Homde; 23.12.2009
comment
Действительно, для небольших объемов данных более целесообразно кэшировать их в памяти. Я предложил чтение файлов, поскольку это более общее решение. Кэширование памяти можно добавить в качестве оптимизации (никогда не знаешь, сколько данных кто-то захочет десериализовать :)) - person Vadym Stetsiak; 23.12.2009

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

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

person Paolo    schedule 23.12.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?

person Florim Maxhuni    schedule 20.12.2009
comment
Сериализация xml работает так же, но часть накладных расходов, вероятно, связана с большим количеством относительных небольших объектов, поэтому создание объекта становится проблемой. Тем не менее сериализация XML почти никогда не бывает быстрее, чем двоичная, и намного более многословна, что приводит к большему количеству времени из-за файла io. - person Homde; 20.12.2009