はじめに
Rustで使えるバイナリシリアライザの性能を簡単にですが計測して比較してみたので、結果を共有します。
計測対象のシリアライザ
以下の要件を満たすシリアライザの性能比較をします。
- バイナリにシリアライズできること
- Serdeを使ってstructやenumに
#[derive(Deserialize, Serialize)]
をつけるだけで利用できること - 比較的複雑なstructやenumもシリアライズできること
ネットを漁ったら以下の3つのシリアライザが見つかったので、これらを対象とします。
zerocopyはC likeなenumしか扱えないみたいなので対象外。protocol buffer系のシリアライザは#[derive(Deserialize, Serialize)]
をつけるだけで利用可能なお手軽な実装が見つからなかったのでこれも対象外。
計測方法
以下のenumに16MBのVec<u8>
をBigDataにいれてシリアライズ・デシリアライズにかかる時間を計測しました。
#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub enum TestEnum {
BigData { data: Vec<u8> },
SmallData { data: u8 },
}
結果
シリアライズ | デシリアライズ | |
---|---|---|
bincode | 70.3723ms | 71.2949ms |
cbor | 105.8754ms | 89.4747ms |
rmp serde | 133.3205ms | 87.1423ms |
見直し
bincodeが速いという結果ですが、16MBのデータにしては遅いですね(※リリースビルドで計測してます)。。
他に速いシリアライザは無いか探してたらserde_bytesというものを見つけました。これは、Vec<u8>
や&[u8]
をフィールドに持つstructのフィールドの宣言の上に#[serde(with = "serde_bytes")]
をつけるだけでシリアライズ・デシリアライズを高速化してくれるもののようです。
先程のenumでも使えました:
#[derive(Debug, PartialEq, Deserialize, Serialize)]
pub enum TestEnum {
BigData {
#[serde(with = "serde_bytes")]
data: Vec<u8>,
},
SmallData {
data: u8,
},
}
これでもう一度計測し直します。
結果(serde_bytes付き)
シリアライズ | デシリアライズ | |
---|---|---|
bincode | 4.8973ms | 2.3063ms |
cbor | 4.2206ms | 2.7095ms |
rmp serde | 3.8015ms | 2.4234ms |
かなり速くなりました!
まとめ
-
Vec<u8>
をフィールドに持つstruct, enumをシリアライズ・デシリアライズする場合にはserde_bytesを忘れずに - bincode, cbor, rmp serdeの中だとrmp serdeがやや速いが、大きな違いはなさそう