C++ のシリアル化ライブラリ
このチュートリアルでは、さまざまな C++ シリアル化ライブラリについて学習します。
まず、シリアル化と C++ でのその目的について学習します。 次に、C++ のシリアライゼーション ライブラリと、それらをプログラムで使用する方法について説明します。
シリアル化の概要
プログラマーは、日常的にメモリ内のデータ オブジェクトを操作します。 また、プログラムの状態の一部を保存するために、オブジェクトをネットワーク経由で送信するか、永続ストレージ (通常はファイル) に書き込む必要がある場合もあります。
シリアル化は、データまたはオブジェクトの状態をバイナリ形式に変換するプロセス/手法です。 バイナリ形式は、オブジェクトの状態を格納/保存するため、またはコンピュータ ネットワークを介してメモリ、データベース、ファイル、またはディスクに転送するためのバイト ストリームです。
シリアライゼーション プロセスの逆は、デシリアライゼーションと呼ばれます。 シリアル化は、プログラムの実行中または実行後に構造化データ (つまり、C++ クラスまたは構造体) の状態を維持する場合に最適です。
serialize()
と deserialize()
については、この ウェブサイト の例で読むことができます。
シリアル化は、ソフトウェア オブジェクトの値の安定したバイト表現を提供します。 その後、これらのバイトは、異なるハードウェアおよびソフトウェアを使用する将来の実装でも正しく機能し続けるネットワークを介して送信できます。
さまざまな言語でのシリアル化
先に進む前に、シリアル化プロセスは言語ごとに異なる方法で実装されています。 以下でそれらを調べてみましょう。
- Java では、直列化メソッドは
writeObject
であり、ObjectOutputStream
に実装されています。 - Python では、シリアル化メソッドは
pickle.dumps()
です。 - Ruby では、シリアル化メソッドは
marshal.dump()
です。 - C++ では、メソッドは
boost::archive::text_oarchive
a (ファイル名); です。a << データ;
シリアル化メソッドを呼び出す - MFC (Microsoft Foundation Class Library) では、シリアル化の方法は次のとおりです。
CObject
からクラスを駆動します。
私たちの主な焦点は C++ シリアライゼーションなので、それがどのように機能するか見てみましょう。
C++ Boost.Serialization は、テキスト アーカイブ オブジェクトを使用します。 シリアル化は、出力データ ストリームとして動作する出力アーカイブ オブジェクトに書き込みます。
クラス データ型に対して呼び出されると、>>
出力演算子はクラスを呼び出して関数をシリアル化します。 各シリアル化関数は、&
演算子を使用するか、>>
を介して、ネストされたオブジェクトを再帰的にシリアル化し、そのデータ メンバーを保存またはロードします。
C++ でのシリアル化をよりよく理解するには、この Web サイト を参照してください。
C++ のシリアル化ライブラリ
C++ には、Boost シリアライゼーション以外のシリアライゼーション用のライブラリが多数用意されています。 すべてのライブラリが高いシリアライゼーション パフォーマンスを実現するのに役立ちます。そのため、いくつかを調べてみましょう。
プロトブフ
プロトコル バッファー (Protobufs)、データのシリアル化に使用されるクロスプラットフォーム。 ネットワークを介したプログラム間の通信に役立ちます。
Protobuf シリアル化メカニズムは、プロトコル アプリケーションを通じて提供されます。 このコンパイラは、.proto
ファイルを解析し、出力として、その引数によって構成された言語 (この場合は C++) に従ってソース ファイルを生成します。
この リンク のチュートリアルを読んで、Protobufs をよりよく理解することができます。
フラットバッファ
FlatBuffers は、最大のメモリ効率を達成するために使用されるオープンソースのクロスプラットフォームです。 前方/後方互換性を使用して、解析せずにシリアル化されたデータに直接アクセスできます。
FlatBuffers を使用して、最初に --CPP
オプションを使用して C++ スキーマを生成します。 次に、ファイルに Flatbuffer を含めて、この生成されたコードを読み書きできます。
この リンク で、FlatBuffers を使用したシリアライゼーションの実装を見つけることができます。
穀物
Cereal は、ヘッダーのみの C++ 11 シリアル化ライブラリです。 Cereal は任意のデータ型を取り、コンパクトなバイナリ エンコーディング、XML、JSON などのさまざまな表現に可逆的に変換します。
Cereal は拡張可能で、高速で、単体テストが行われており、Boost のような使い慣れた構文を提供します。 完全なシリアル ライブラリをダウンロードするには、ここ をクリックします。
次に、シリアル ライブラリの使用例を示します。
#include <cereal/archives/binary.hpp>
#include <cereal/types/memory.hpp>
#include <cereal/types/unordered_map.hpp>
#include <fstream>
struct MyRecord {
uint8_t x, y;
float z;
template <class Archive>
void serialize(Archive& ar) {
ar(x, y, z);
}
};
struct SomeData {
int32_t id;
std::shared_ptr<std::unordered_map<uint32_t, MyRecord>> data;
template <class Archive>
void save(Archive& ar) const {
ar(data);
}
template <class Archive>
void load(Archive& ar) {
static int32_t idGen = 0;
id = idGen++;
ar(data);
}
};
int main() {
std::ofstream out("out.cereal", std::ios::binary);
cereal::BinaryOutputArchive archive(out);
SomeData myData;
archive(myData);
return 0;
}
プライマリ関数では、バイナリ モードでバイナリ ファイル名 os.cereal
を作成します。
次の行では、シリアル ライブラリのアーカイブ フォルダーに定義されている binary.hpp
からクラス BinaryOutputArchive
のオブジェクトを作成します。 BinaryOutputArchive
のコンストラクターにファイル オブジェクトを渡します。
次に、シリアル化するデータ オブジェクト、クラス some data のオブジェクトを作成します。 最後に、プライマリ関数の 4 行目で、データ オブジェクトをアーカイブに渡します。間接的に save
関数を呼び出して出力ファイルにデータを保存し、シリアル化されたオブジェクトを出力ファイル out.cereal
に保存します。
バイナリ ファイル out.cereal
にはシリアル化されたデータが含まれています。 このコードの出力は、ファイル out.cereal
のデータになります。
このデータはバイナリ形式であり、さまざまなオペレーティング システムで利用可能なさまざまなユーティリティ/コマンドを使用して確認する場合があります。
HPS
HPS は高性能シミュレーション ツールであり、C++11 でのデータのシリアル化のためのヘッダーのみのライブラリです。 HPS は、データを圧縮形式にエンコードして、ネットワークを簡単に通過できるようにします。
HPS は、C++ での通常のブースト シリアル化よりも 150% 高速です。これは、標準テンプレート ライブラリとプリミティブ データ型に必要なコードが 1 行だけであるためです。
write to_stream
および from_stream
関数を使用して、ファイルのデータを読み書きすることもできます。 HPS の理解を深めるには、この Web サイト も参照してください。
GitHub メッセージパック
MessagePack は、オープン ソース固有のシリアル化形式です。 JSON や XML などのさまざまな形式のデータを簡単に交換できます。
整数値はデータをエンコードするために 1 バイトしか必要としませんが、文字列値は余分なバイトを必要とします。
msgpack のさらなる実装については、リンク にアクセスしてください。
Boost.Serialization
C++ の Boost Serialization ライブラリを使用すると、オブジェクトを保存および読み込みのためにバイトに変換して復元することができます。 さまざまな形式で、一連のバイトが生成されます。
これらの形式はすべて Boost でサポートされています。 シリアル化は、このライブラリでの使用のみを目的としています。
C++ では、シリアル化する各オブジェクトに serialize
メソッドを実装する必要があります。 引数としてアーカイブを取る必要があります。 アーカイブは、入出力データ ストリームに似ています。
演算子 <<
または >>
を使用する代わりに、一般的な演算子を使用して、ボットの保存および読み込み操作を処理できます。 この ウェブサイト でブーストのシリアル化について読むことができます。
アパッチ・アブロ
Apache Avro は、データのシリアル化システムです。 Avro C++ は、Avro 仕様を実装する C++ ライブラリであり、ライブラリはストリーミング パイプラインで使用することを目的としています。
たとえば、Apache Kafka は、集中管理されたスキーマを使用してデータのシリアル化と逆シリアル化を実行します。
Avro はコード生成を必要としません。 コード生成ツールを使用できます。 コード ジェネレーターはスキーマを読み取り、スキーマのデータを表す C++ オブジェクトを .schema
で出力します。
また、このオブジェクトをシリアル化および逆シリアル化するコードも作成します。 ここでは、すべての重いコーディングが自動的に行われます。
コア C++ ライブラリを使用してカスタム シリアライザーまたはパーサーを記述したい場合でも、生成されたコードはこれらのライブラリの使用方法の例になります。
このスタイルは機能しますが、完璧なソリューションのために、構造体またはクラスをシリアライズ可能にすることができます。
キャプテン・プロト
Cap’n Proto は RPC (Remote Procedure Call) システムに依存しているため、データ形式を簡単に交換できます。 Cap'n Proto
にはエンコード/デコードの概念はありません。
データ交換フォーマットはエンコーディングとして機能し、メモリを表します。 データを構造化することで、ディスクから直接バイトを簡単に書き込むことができます。
Cap’n Proto ページで最良の例を見つけることができます。
倹約
Apache Thrift は、言語の問題に焦点を当てたシリアル化フレームワークです。 IDL (Interface Definition Language) で抽象データ型を定義できます。これは、サポートされている言語のソース コードにさらにコンパイルされます。
次に、これらの生成されたユーザー定義型のコードによって、完全なシリアル化が提供されます。 Apache Thrift は、任意のデータ型が読み取りまたは書き込み可能であることを保証します。
この ページ で Apache Thrift の最良の例を見つけることができます。
ヤス
YAS は、シリアル化速度が遅いため、Boost.serialization の代替として作成されます。 これはヘッダーのみのファイルでもあり、サードパーティのライブラリは必要ありません。 バイナリ、テスト、および JSON 形式をサポートし、C++11 が必要です。
YAS については、この ページ で読むことができます。
すべてのシリアライゼーション ライブラリの比較
これらのライブラリはすべてシリアル化を提供し、さまざまな期間にライブラリを持っています。 これらのライブラリの結果を示しているだけですが、コードと詳細については、この ページ で確認できます (同じ行は前の行で既に共有されています)。
この記事では、記事から取得した結果を示しています。
このコードは、Ubuntu 16.04 を実行する Intel Core i7 プロセッサを搭載した一般的なデスクトップ コンピューターで実行され、平均時間が計算されています。
シリアライザ | オブジェクトのサイズ | 平均合計時間 |
---|---|---|
倹約バイナリー | 17017 | 1190.22 |
コンパクト | 13378 | 3474.32 |
プロトブフ | 16116 | 2312.78 |
ブースト | 17470 | 1195.04 |
メッセージパック | 13402 | 2560.6 |
穀物 | 17416 | 1052.46 |
アブロ | 16384 | 4488.18 |
ヤス | 17416 | 302.7 |
yas-コンパクト | 13321 | 2063.34 |
Cap’n Proto と Flatbuffers は、データをシリアル化された形式で保存します。シリアル化とは、内部ストレージへのポインターを取得することを意味します。 したがって、Cap’n Proto では、ビルド/シリアライズ/デシリアライズのサイクル全体を測定します。
他のライブラリの場合、既に構築されたデータ構造のサイクルをシリアライズ/デシリアライズすることもできます。
シリアライザー | オブジェクトのサイズ | 平均 合計時間 |
---|---|---|
capnproto | 17768 | 400.98 |
フラットバッファ | 17632 | 491.5 |
サイズに応じた上記のデータ表現を見ると、YAS は他のライブラリよりも多くのオブジェクト サイズを使用します。 それでも、シリアル化の時間を考慮すると、YAS は時間がかかりません。
したがって、YAS はすべてのライブラリの中で最速のシリアル化を示します。
注: サイズはバイト単位で測定され、時間はミリ秒単位で測定されます。
シリアライゼーションとその使用法を理解していただければ幸いです。 これで、C++ でシリアル化を実行できるライブラリ、または最も高速に実行できるライブラリがわかりました。