さて、github で週末ちまちま書いていた MessagePack for CLI が、公式のリポジトリに取り込まれました。びっくりです。
MessagePack とは
相互運用可能で、コンパクトなバイナリ形式のオブジェクトシリアル化フォーマットまたはそのライブラリです。詳しくは 公式サイト なり、 @frsyuki さんのブログなどを参照してください。今回はそれの .NET 用の実装を作った(ている)ということになります。
.NET 用の実装について
.NET 用の実装は実は 3 代目だったりします。初代は @neuecc さんの blog のエントリで使用されているもの。わりとざっくりとした実装で、まだまだパフォーマンスチューニングの余地がある状態でした。2 代目は NuGet で MessagePack for C# で出てくるもの。こちらは去年の春に出てきたもので、IL 何かを使って高速化されてます。ちなみに、オブジェクトは Map(Dictionary)としてシリアル化されます。パフォーマンスはかなり高く、場合によっては protobuf-net 以上になるようです。
3 代目が for CLI で、こちらが私が作ったものになります。パフォーマンスなどについては後述します。説明についてはつたない英語ですが wiki があります
MessagePack for CLI という名前の理由
よく誤解されてるんですが、.NET 系の言語は、CLS(共通言語仕様)に準拠しているライブラリを作れば、CLI (共通言語基盤)上で動作する他の言語でも実行できます(.NET の FCL(Framework Class Library)も Mono の FCL も大部分は C# で記述されていますが、VB や F# から問題なく呼び出せているはずです)。なので、「for C#」もないかなぁと思って作り始めました。そのため、MessagePack for CLI は VB、F#、C++/CLI、PowerShell、IronRuby、IronPython などからも呼び出すことができます(FAQ に補足あります)。そして、MessagePack がうれしいのって、どちらかというと Web の世界の方なのかと思い、Silverlight にも対応した方が良いよねとか(始めたときは 2010 年の夏ですしね)、Mono を除外する理由はないよねといったところで、for CLI にしました。たとえば、MessagePack-RPC のサーバーとして Mono を載せた Linux がいてもいいはずです。今は Unity もありますしね。
MessagePack-RPC
MessagePack-RPC というのもありまして、こちらは MessagePack を使用してシリアル化したメッセージを呼び出すフレームワークです。MessagePack-RPC for CLI もあります。Silverlight でも簡単な動作確認を行っています。これも詳細については 公式サイトやwikiをご覧ください。入手方法
プレリリース版として NuGet に上がっていますので、ご利用ください。MessagePack for CLI というパフォーマンス
前述の@neuecc さんの blog のエントリについて、自宅のマシン(Windows 7、.NET 4、x86 リリースビルド、Core i7 3770 Quad Core 3.4GHz)で測ったところ、以下のような感じです。なお、XmlSerializer についてはそもそもシリアル化できていないので割愛しました。また、逆シリアル化後のチェックロジックで DateTime 型の比較については UTC に変換し、マイクロ秒以下を無視して比較しています(端から UTC にすると JSON.NET がうまくいかなかったりとか、マイクロ秒以下が MessagePack では合わないとかあるので)。protobuf-net にシリアル化のパフォーマンスは肉薄できているかなと思います。サイズは MessagePack の方が有利です。Serialize BinaryFormatter 00:00:07.3377291 53MB Serialize DataContractSerializer 00:00:02.2492265 149MB Serialize DataContractSerializer Binary 00:00:01.5054592 78MB Serialize DataContractJsonSerializer 00:00:02.7829966 47MB Serialize DataContractJsonSerializer Binary 00:00:02.7813589 75MB Serialize NetDataContractSerializer 00:00:12.2300278 183MB Serialize NetDataContractSerializer Binary 00:00:10.5490878 99MB Serialize Formatter - Protocol Buffers 00:00:00.5668601 13MB Serialize AutoMessagePackSerializer`1 - MessagePack 00:00:00.6150166 11MB Serialize JsonSerializer - JSON.NET 00:00:08.5988969 38MB Serialize JsonSerializer - JSON.NET BSON 00:00:12.5111917 44MB Deserialize BinaryFormatter 00:01:07.9430564 Deserialize XmlSerializer 00:00:02.8090175 Deserialize DataContractSerializer 00:00:06.9147897 Deserialize DataContractSerializer Binary 00:00:03.7894125 Deserialize DataContractJsonSerializer 00:00:12.3970537 Deserialize DataContractJsonSerializer Binary 00:00:06.2072323 Deserialize NetDataContractSerializer 00:00:09.4501272 Deserialize NetDataContractSerializer Binary 00:00:07.3537588 Deserialize Formatter - Protocol Buffers 00:00:01.0971126 Deserialize AutoMessagePackSerializer`1 - MessagePack 00:00:05.0464446 Deserialize JsonSerializer - JSON.NET 00:00:12.3115143 Deserialize JsonSerializer - JSON.NET BSON 00:00:12.2420603
ところが、逆シリアル化は protobuf-net よりもずいぶん時間がかかっています。ここは改善の余地があります(ちなみに、2 代目のパフォーマンスをはかると、そもそもシリアル化と逆シリアル化でパフォーマンスの差がここまで開いていないので、フォーマットの差というよりは MessagePack for CLI の逆シリアル化の実装に問題があると考えています。というか、Map モードで for CLI 使った場合、逆シリアル化では 2 代目の方が数倍速いです)。
はまりどころ
使ってみるとハマるかもしれない仕様についていくつか言っておきます(使ってみてがっかりされてしまうのは嫌ですし)。相互運用性のため(他にも C++、Java、D、Erlang、Ruby、Python、Haskell、Go などなど色々あります)、次の仕様上の制限があることをご承知おきくださいませ。- 文字列は既定では utf-8(BOM なし)になります。Encoding を指定すれば上書き可能です(相互運用性はなくなります)。
- DateTime/DateTimeOffset は UTC の Unix Epoc になります。マイクロ秒以下の値とタイムゾーン情報は失われます。 これを回避するには、独自の MessagePackSerializer
を定義するとか、ToString("o") するとかする必要があります。 - オブジェクトは配列としてシリアル化されます。Map ではありません。そのため、長期間保存する場合、うっかりソースコードで型のフィールドの順番(MessagePackMemberAttribute カスタム属性で順序を明示的に指定できます)を変えたりすると逆シリアル化できなくなります。これは SerializationContext.SerializationMethod オプションで変更できます。
FAQ
- NuGet は?
- 公開済みです。ただし、現在のところプレリリース版です。
- 今後の予定は?
- とりあえず品質の安定、逆シリアル化のパフォーマンスの向上、Silverlight/Phone での自動テストの追加、WinRT サポート、IDL サポートくらいでしょうか。
IDL は言語(C# や VB)ごとに Haskell 版のパーサーに Pull Request するのがいいのか、自前で作るのが良いのかちょっと悩んでいます。GHC なんかは簡単にインストールできますが、.NET 以外入れたくない、という方もいるでしょうし。
それ以外はノーアイディアなので、気付いた点などあればご連絡いただけると嬉しいです。(Unpacker の効率改善案とか) - WinRT 対応予定は?
- ちまちまやってますが、テストをどうしたものか、といったところです。
- PowerShell/IronRuby/IronPython から使いづらいんですが
- 『.NET のクラスライブラリ設計』を訳しておきながら恐縮ですが、動的言語についてはあまり考えずに API を設計していますので、使いづらい面もあるかと思います(型引数とかそもそもの API のスタイルとか)。IronRuby、IronPython については公式リポジトリの Ruby 版や Python 版を試してみてください。もちろん、何か改善案があれば、ぜひ教えていただけると嬉しいです。
- F# の API が気に食わない。お前これ名古屋でも F# 対応って言えるの?
なごやこわい。MessagePack for F# を作るなら今です。早くしないと出張中(ただし世間は GW)のホテルで帰ってきたら先を越されてへこむとかいう事態になりかねません。もちろん、改善で対応できるかもしれませんし、改善案があればいただけると嬉しいです。- 戻り値が IEnumerable<T> じゃないのはおかしい
- MessagePack for LINQ や MessagePack-Rx とか作ればいいじゃない。
- Mono for Android や Mono Touch では動きますか?
- レポートお待ちしております m(_ _)m
2012/6/21 Code Contract 周りでそもそもビルドできない問題があるようですが、次の MfA ではビルドできそうです。もちろん、ご意見等歓迎です。