[C#]MemoryPackを使ってみたい1 - 基本

Cysharpから新しいシリアライザがリリースされました。

Cysharp/MemoryPack

過去にASP.NET CoreのAPIサーバーとのやりとりで、なんか効率の上がる方法を模索していたときにMessagePackなんかにチャレンジしていたりしましたが、クライアント側がXamarinであったり、非C#なWebだったりしたこともあり手間とか考えて断念した過去がありました。 ところがこのMemoryPack、mpcとかいらなくなったし、なんならTypeScript用のクライアントまで出せるようになってるということで、当時これがあったら絶対使ってました。

一般的にはAPIサーバーとのやりとりはJSONで行われることが多いと思いますが、これはだいたいクライアント側がJavaScript系の技術を使っているから親和性が高いとか 流れてるデータをRESTクライアント的なものでのぞいてみたりするのが楽だとか、データの構造やフィールド名、ある程度の型なんかも人間の目で見てもわかりやすいからとか、公開されて多くの人が使うようなAPIなので共通言語的な感覚で採用されてるとか、みんな昔からそうしてるからとか、色々理由があって根付いてるやり方だと思います。

ところが、サーバーとアプリの間の閉じられた空間だけでやりとりするデータであれば、JSONである必要はないはずです。

例えばある企業の業務を支援するようなシステムで、Webやモバイルをクライアントとして活用するようなケースでは流れてるデータの可読性なんてどうでもいい話で、むしろAPIサーバーとのやりとりの効率を上げた方がいいはずですね。 (昔ちょっと触ったシステムで、データのサイズをケチるためにフィールド名が記号化されていたのだけど、データをよく見たら「使ってないフィールド何個か消した方がはるかに小さくなるよね…」みたいな思い出を添えて)

それをシリアライザを置き換える程度の作業でできるのであればやってみる価値は十分あります。 そう、MemoryPackを使ってみるだけで、変な壺を買うより幸せになれるはずです。

基本

まずはシリアライズとデシリアライズの基本。

とりあえず新規のConsole App(.NET 6)にMemoryPackをインストールして…

PM> Install-Package MemoryPack

Program.csをこうして…

using MemoryPack;

var bin = MemoryPackSerializer.Serialize(new TestClass
{
    Id = 100,
    Name = "Hello"
});

var obj = MemoryPackSerializer.Deserialize<TestClass>(bin);

if (obj != null)
    Console.WriteLine($"{obj.Id}:{obj.Name}");

[MemoryPackable]
public partial class TestClass
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
}

実行

100:Hello

シリアライズしたいクラスをpartialにして、MemoryPackable属性をつけるだけ。あとはJSONなどと同じようにシリアライズ/デシリアライズするだけです。

注意事項

フォーマットとしては、フィールドの値(と型)だけを取り出して前から順番に詰め込んでいるので、MemoryPackableなクラスに対して、

というような変更をしてしまうと、デシリアライズできなくなります。なのですでに稼働しているシステムのデータ構造を変更する場合は注意が必要です。

ただし下記は大丈夫です。

※追記: 2022/11/10

ver1.5.0で、この辺の制限を緩和する機能が入りました。→MemoryPackを使ってみたい5 - VersionTolerant

まとめ

よくアプリ等でデータを保存しておく場合に、JSONにシリアライズしてテキストファイルで保存するケースも多いかと思いますが、そういうのをMemoryPackに置き換えることができますね。

まあ、そういう操作の場合はそこまで速度はいらないと思いますが、それでもJsonResponseModelのグラフを見ればわかるとおり、.NETのJsonSerializerと比べても20倍ぐらい速いので、でかいデータを扱っている場合は体感できるぐらいの違いがあるかもしれません。何らかの理由で頻繁にファイルに吐き出すようなケースではじわじわ効いてくるでしょう。またファイルサイズも小さくなるのであれば、読み書きにかかる時間も減るってことですし困ることなんか一つもありません。

「ファイルに書き出すが、そのファイルはアプリでしか使わない」なんていうケースではJSONでもバイナリでもどっちも一緒ですし、.NETのBinaryFormatterが廃止予定になっているのでそういったものの置き換えになる場合もあるかもしれません。なにしろシンプルなクラスを扱ってる限りは[MemoryPackable]をつけるだけですみますから。

さて、基本的な使い方はわかったので、次はASP.NET Coreで使ってみます。