Подтвердить что ты не робот

Делаете ли зерно и Boost Serialization нулевой копией?

Я провел сравнение производительности нескольких протоколов сериализации, включая FlatBuffers, Cap'n Proto, Boost serialization и зерновые. Все тесты написаны на С++.

Я знаю, что FlatBuffers и Cap'n Proto используют нуль-копию. При нулевом копировании время сериализации равно нулю, но размер сериализованных объектов больше.

Я думал, что сериализация зерновых и Boost не использует нулевую копию. Однако время сериализации (для int и double) почти равно нулю, а размер сериализованных объектов почти такой же, как у Cap'n Proto или Flatbuffers. Я не нашел никакой информации о нулевой копии в своих документах.

Нужно ли использовать сериализацию зерновых и Boost с нулевой копией?

4b9b3361

Ответ 1

Boost и Cereal делают не реализовать нулевую копию в смысле Cap'n Proto или Flatbuffers.

При использовании истинной нулевой копии, хранилище резервных копий для ваших живых объектов в памяти на самом деле является точно таким же сегментом памяти, который передается системным вызовам read() или write(). Отсутствует шаг упаковки/распаковки.

Как правило, это имеет ряд последствий:

  • Объекты не выделяются с помощью new/delete. При создании сообщения сначала вы отправляете сообщение, которое выделяет длинное непрерывное пространство памяти для содержимого сообщения. Затем вы выделяете структуру сообщения непосредственно внутри сообщения, получая указатели, которые на самом деле указывают на память сообщений. Когда сообщение будет позже написано, один вызов write() выталкивает все это пространство памяти в провод.
  • Аналогично, когда вы читаете сообщение, один вызов read() (или, возможно, 2-3) читает во всем сообщении в один блок памяти. Затем вы получаете указатель (или объект, похожий на указатель) на "корень" сообщения, который вы можете использовать для его перемещения. Обратите внимание, что никакая часть сообщения не проверяется до тех пор, пока ваше приложение не пересечет его.
  • С обычными сокетами только копии ваших данных происходят в ядро ​​. В сетях RDMA вы даже можете избежать копирования в пространстве ядра: данные выводятся из провода непосредственно в конечную ячейку памяти.
  • При работе с файлами (а не сетями) возможно mmap() получить очень большое сообщение непосредственно с диска и напрямую использовать область отображенной памяти. Это значит, что O (1) - неважно, насколько велик файл. Ваша операционная система автоматически выведет страницы в нужные части файла, когда вы действительно получите к ним доступ.
  • Два процесса на одном компьютере могут взаимодействовать через сегменты разделяемой памяти без копий. Обратите внимание, что обычно обычные старые объекты С++ плохо работают в общей памяти, поскольку сегменты памяти обычно не имеют одинакового адреса в обоих пространствах памяти, поэтому все указатели ошибочны. С каркасом сериализации с нулевой копией указатели обычно выражаются как смещения, а не абсолютные адреса, поэтому они не зависят от позиции.

Boost and Cereal различаются: когда вы получаете сообщение в этих системах, сначала выполняется вскрытие всего сообщения, чтобы "распаковать" содержимое. Конечное место отдыха данных в объектах, выделенных традиционным способом, с использованием new/delete. Аналогичным образом, при отправке сообщения данные должны собираться из этого дерева объектов и упаковываться вместе в один буфер, чтобы их можно было выписать. Несмотря на то, что Boost и Cereal являются "расширяемыми", для действительно нулевой копии требуется совершенно другой базовый дизайн; он не может быть прикреплен болтами в качестве расширения.

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

Место, где нулевая копия наиболее очевидно выгодна, - это манипулировать файлами, поскольку, как я уже говорил, вы можете легко mmap() создать огромный файл и только прочитать его часть. Формат без нулевой копии просто не может этого сделать. Однако, когда дело доходит до сетей, преимущества менее ясны, так как сама коммуникация сети - это O (n).

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

Раскрытие информации: Я являюсь автором Cap'n Proto (сериализатор с нулевой копией) и протокольных буферов v2 (популярный сериализатор без нулевой копии).

Ответ 2

Расширение Serialization расширяется.

Он позволяет вашим типам описывать, что должно быть сериализовано, и архивы для описания формата.

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

Для примера осознанно нулевой копии сериализации для dynamic_bitset см. код в этом ответе: Как сериализовать boost:: dynamic_bitset?

У меня есть несколько из них на сайте. Также просмотрите документацию для BOOST_IS_BITWISE_SERIALIZABLE и эффект, который она оказывает на сериализацию контейнера (если вы сериализуете смежно выделенную коллекцию побитово сериализуемых данных, результат равен нулю или даже __memcpy_sse4 и т.д.).

Боковая заметка: Cap'n proto делает что-то еще полностью, AFAIK: он марширует некоторые объекты как фьючерсы на данные. Это, по-видимому, то, что они рекламируют агрессивно, как "∞% быстрее, 0μs!!!" (что несколько верно в случае, когда данные никогда не извлекаются).