быстрая сериализация c ++? - PullRequest
       28

быстрая сериализация c ++?

18 голосов
/ 03 сентября 2010

Доброе утро всем,

Я ищу очень быструю методику двоичной сериализации для c ++. Мне нужно только сериализовать данные, содержащиеся в объектах (без указателей и т. Д.). Я хотел бы, чтобы это было так быстро, как возможный. Если это специфично для оборудования x86, это приемлемо.

Я знаком с методами C, делающими это. В качестве теста у меня на скамейке было отмечено несколько приемов. Я обнаружил, что метод C на 40% быстрее, чем лучший метод C ++, который я реализовал.

Есть предложения по улучшению метода C ++ (или библиотек, которые делают это)? Что-нибудь хорошее для файлов с отображенной памятью?

Спасибо

// c style writes
{
   #pragma pack(1)
   struct item
   {
      uint64_t off;
      uint32_t size;
   } data;
   #pragma pack

   clock_t start = clock();

   FILE* fd = fopen( "test.c.dat", "wb" );
   for ( long i = 0; i < tests; i++ )
   {
      data.off = i;
      data.size = i & 0xFFFF;
      fwrite( (char*) &data, sizeof(data), 1, fd );
   }
   fclose( fd );

   clock_t stop = clock();

   double d = ((double)(stop-start))/ CLOCKS_PER_SEC;
   printf( "%8.3f seconds\n", d );
}

около 1,6 секунд для тестов = 10000000

// c++ style ofstream writes

// define a DTO class
class test
{
public:
   test(){}

   uint64_t off;
   uint32_t size;

   friend std::ostream& operator<<( std::ostream& stream, const test& v );
};

// write to the stream
std::ostream& operator<<( std::ostream &stream,  const test& v )
{
   stream.write( (char*)&v.off, sizeof(v.off) );
   stream.write( (char*)&v.size, sizeof(v.size) );
   return stream;
}

{
   test data;

   clock_t start = clock();

   std::ofstream out;
   out.open( "test.cpp.dat", std::ios::out | std::ios::trunc | std::ios::binary );
   for ( long i = 0; i < tests; i++ )
   {
      data.off = i;
      data.size = i & 0xFFFF;
      out << data;
   }
   out.close();

   clock_t stop = clock();

   double d = ((double)(stop-start))/ CLOCKS_PER_SEC;
   printf( "%8.3f seconds\n", d );
}

около 2,6 секунд для тестов = 10000000

Ответы [ 11 ]

14 голосов
/ 03 сентября 2010

Всего лишь очень мало реальных случаев, когда это вообще имеет значение.Вы когда-либо сериализуетесь, чтобы сделать ваши объекты совместимыми с каким-то внешним ресурсом.Диск, сеть и так далее.Код, который передает сериализованные данные на ресурс, всегда на несколько порядков медленнее, чем код, необходимый для сериализации объекта.Если вы сделаете код сериализации в два раза быстрее, вы в целом выполняете операции не более, чем на 0,5% быстрее, отдайте или возьмите.Это не стоит ни риска, ни усилий.

Измерьте три раза, отрежьте один раз.

5 голосов
/ 03 сентября 2010

Если задача, которую нужно выполнить - это действительно сериализация, вы можете проверить Protocol Buffers от Google .Они обеспечивают быструю сериализацию классов C ++.На сайте также упоминаются некоторые альтернативные библиотеки, например boost.serialization (разумеется, только для того, чтобы утверждать, что буферы протокола превосходят их в большинстве случаев ;-)

3 голосов
/ 02 сентября 2012

Средство записи C ++ Middleware - это онлайн-альтернатива библиотекам сериализации. В некоторых случаях это быстрее, чем библиотека сериализации в Boost.

2 голосов
/ 11 апреля 2015

Google Flatbuffers, похожий на буфер протокола, но намного быстрее

https://google.github.io/flatbuffers/

https://google.github.io/flatbuffers/md__benchmarks.html

2 голосов
/ 03 сентября 2010

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

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

class Buffer
{
public:
  inline Buffer& operator<<(int i); // etc...
private:
  std::deque<unsigned char> mData;
};

Должен признать, я не понимаю вашу проблему:

  • Что вы на самом деле хотите сделать с сериализованным сообщением?
  • Вы сохраняете его на потом?
  • Вам нужно беспокоиться о перемотке вперед / назад?совместимость?

Может быть, лучше подходить к сериализации.

1 голос
/ 12 января 2017

Поскольку наиболее вероятным узким местом может быть ввод / вывод, может помочь компактный формат.Из любопытства я попробовал следующую схему Колфера, скомпилированную как colf -s 16 C.

    package data

    type item struct {
            off  uint64
            size uint32
    }

... с сопоставимым тестом C:

    clock_t start = clock();

    data_item data;
    void* buf = malloc(colfer_size_max);

    FILE* fd = fopen( "test.colfer.dat", "wb" );
    for ( long i = 0; i < tests; i++ )
    {
       data.off = i;
       data.size = i & 0xFFFF;
       size_t n = data_item_marshal( &data, buf );
       fwrite( buf, n, 1, fd );
    }
    fclose( fd );

    clock_t stop = clock();

Результаты на SSD довольно разочаровывающие, несмотря натот факт, что размер серийного номера на 40% меньше по сравнению с необработанными дампами структуры.

    colfer took   0.520 seconds
    plain took    0.320 seconds

Поскольку сгенерированный код довольно быстрый , кажется, вряд ли вы что-нибудь выиграете с сериализациейбиблиотеки.

1 голос
/ 03 сентября 2010

Большая производительность будет зависеть от буферов памяти и того, как вы заполняете блоки памяти перед записью на диск. И есть некоторые приемы, чтобы сделать стандартные потоки c ++ немного быстрее, например std::ios_base::sync_with_stdio (false);

Но ИМХО, миру не нужна другая реализация сериализации. Вот некоторые, что другие люди утверждают, что вы можете посмотреть:

  • Boost : быстрая, разнообразная библиотека C ++, включая сериализацию
  • protobuf : быстрая кросс-платформенная кросс-языковая сериализация с модулем C ++
  • thrift : Гибкая кроссплатформенная, кросс-языковая сериализация с модулем C ++
1 голос
/ 03 сентября 2010

Если вы работаете в системе Unix, mmap в файле - это способ сделать то, что вы хотите.

См. http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx для эквивалента в Windows.

1 голос
/ 03 сентября 2010

Можно ли как-нибудь воспользоваться тем, что осталось прежним?

Я имею в виду, вы просто пытаетесь запустить "test.c.dat" так быстро, как только можете, верно? Можете ли вы воспользоваться тем, что файл не меняется между вашими попытками сериализации? Если вы пытаетесь сериализовать один и тот же файл снова и снова, вы можете оптимизировать на основе этого. Я могу сделать так, чтобы первая попытка сериализации заняла столько же времени, сколько и ваша, плюс немного больше для другой проверки, а затем, если вы попытаетесь снова запустить сериализацию на том же входе, я смогу сделать мой второй запуск намного быстрее, чем в первый раз.

Я понимаю, что это может быть просто тщательно разработанный пример, но вы, кажется, сосредоточены на том, чтобы заставить язык выполнить вашу задачу как можно быстрее, вместо того, чтобы задавать вопрос "нужно ли мне выполнить это снова?" Каков контекст этого подхода?

Надеюсь, это полезно.

-Брайан Дж. Стинар-

0 голосов
/ 29 декабря 2017

Чтобы действительно ответить на этот вопрос, причина, по которой версия C ++ медленная, заключается в том, что она вызывает ostream.write слишком много раз, что вызывает огромное количество ненужных проверок состояния. Вы можете создать простой буфер и использовать только один write, и вы увидите разницу.

Если ваш диск / сеть действительно достаточно быстр, чтобы не стать узким местом, flatbuffers capnproto - отличные варианты для вас.

В противном случае, protobuf, xxx-compact ... независимо от того, что используется кодирование с использованием varint, возможно, эти данные будут сериализированы до четверти исходного размера. HPS от научного компьютерного сообщества также является отличным вариантом для этого вида высокоструктурированных данных и, вероятно, наиболее быстрым по скорости и наименьшим размером сообщения в этом случае благодаря своей схеме кодирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...