シリアライザ、シリアライズに使うもの、という意味ですが、シリアライズとはなにかというところから…。
簡単に言えば、ここでのシリアライズとは、データをファイルとして保存する、ということだと思ってください。厳密な定義としてはファイルの形として保存するかどうかは関係ないのですが、今回はファイルの読み書きで使用します。
データを簡単にファイル読み書きするフォーマットとしては JSON が挙げられます。Beat Saber の設定ファイルでもこの形式が使用されており、ファイルの中身は下記のようになっています。
{
"enabled": true,
"position": {
"x": 0.0,
"y": 1.0,
"z": 4.0
}
}
このように人間が見てわかる内容で、テキストエディタで開いて編集することもでき便利です。一方で、コンピューターが扱う形としては、人間向けの形として読み込み、書き込みする分、不要な処理が入るため、速度や効率に関しては最適とは言えません。
人間が手動で中身を読み書きする必要がないフォーマットの場合、JSON を使用する必要はありません (実使用上に問題がなければ使用する選択肢もあります)。このような場合によく使われるのが、高速シリアライザの類です。
Beat Saber ModAssistant の Libraries のところには protobuf-net というものが入っていますが、これも高速シリアライザのひとつで、Beat Saber に限定されたものではなく、さまざまなプログラミング言語で使用できる汎用的なシリアライザです。
https://github.com/protobuf-net/protobuf-net
今回はこれと、もうひとつ、protobuf-net の後にでてきた、MessagePack というものを比較検討していきます。
https://github.com/MessagePack-CSharp/MessagePack-CSharp
MessagePack 配布元によるベンチマークでは、protobuf-net よりも高速でバイナリサイズも小さくなる、という触れ込みですが、このあたりは条件により変わる部分も大きいので、実際に Beat Saber 内の実使用条件で比較してみようと思います。
ちなみに、MessagePack の後にでた、さらに高速という触れ込みの MemoryPack も検討したのですが、こちらは Beat Saber 1.29.1 がベースとしている Unity 2019.4.28 では動作させることができなかったため断念しました。Unity 2021.3.16 がベースの Beat Saber 1.29.4 以降なら動くかもしれませんが…、まだ当分は Beat Saber 1.29.1 の対応を放棄することはできないため、採用できません。
https://github.com/Cysharp/MemoryPack
これらのシリアライザが具体的にどのようにいかされているかというと、たとえば、NalulunaAnimator は起動時に内蔵のアニメーションと、外部ファイルに用意されたアニメーション、これらを読み込む処理があります。
なにも工夫をしないと、内蔵のアニメーションの読み込みだけで 2 秒、また、1 曲分のダンスアニメーションのような巨大な anim ファイルをたくさん用意していた場合は、読み込み時間はさらに 10秒、20秒と増える可能性があります。Beat Saber の起動にそんなに時間がかかるようになってしまっては耐えられませんよね。
そこで、これらのアニメーションを初回に読み込んだあと、高速に読み込むことのできる別の形式として保存しなおしておくことで、次回起動時の読み込み時間を短縮することが可能になります。
それでは、どの程度高速化されるのでしょうか。次のテストをご覧ください。
NalulunaAnimator には標準で内蔵しているアニメーションデータが 16 あります。これらすべてをシリアライズ、デシリアライズしたときの合計時間、そしてシリアライズしてファイルとして保存したときの合計ファイルサイズもチェックしてみます。
ディスクへの書き込み時間のぶれを除くため、シリアライズ、デシリアライズはメモリーへのアクセスにしています。また、シリアライズ、デシリアライズ以外の処理のときは時間経過を止めています。
anim パース (読込)
1回目 2049ms
2回目 2048ms
3回目 2073ms
合計ファイルサイズ 25.7 MB
シリアライズ (保存)
1回目 508ms
2回目 556ms
3回目 509ms
デシリアライズ (読込)
1回目 408ms
2回目 368ms
3回目 381ms
合計ファイルサイズ 3.48 MB
シリアライズ (保存)
1回目 68ms
2回目 77ms
3回目 63ms
デシリアライズ (読込)
1回目 77ms
2回目 77ms
3回目 79ms
合計ファイルサイズ 1.95 MB
シリアライズ (保存)
1回目 47ms
2回目 48ms
3回目 48ms
デシリアライズ (読込)
1回目 52ms
2回目 52ms
3回目 51ms
まず Json、なにもせずに anim から読み込むよりは、Json でもキャッシュしたほうが 4 倍ほど速くなっています。anim のパースはけっこう面倒なことをしているので、これでもだいぶ高速化になります。
protobuf-net を使用すると、Json よりもはるかに高速化されます。ファイルサイズもかなり小さくなり、やはり Json のオーバーヘッドが大きいことがわかります。ちなみに、いままでの NalulunaAnimator はこちらを使用しています。
MessagePack は、配布元が謳っているほどの差はありませんでしたが、protobuf-net よりもさらに高速、サイズも小さくなり、今回最良の結果となりました。これ Beat Saber で動くようにするのに、いろいろ試行錯誤で苦労したので、無駄にならずに済みました。良かった…。
このように、工夫をしなければ 2000ms かかる処理が、手を加えることによって 50ms で済むようになり、40 倍の高速化ができたことになります。おもしろいと思いませんか?こむずかしい話もあったと思いますが、最後まで読んでくださって、ありがとうございました。