npm install @msgpack/msgpack でインスコできます。NodeJS v12 でベンチマークしたかぎり、JSONと同程度の速度で、これまで最速といわれてきた msgpack-lite よりもさらに少しだけ高速です。
もともとこのリポジトリには uupaaさんによる実装(tagged as classic
) があったんですが、メンテされなくなって久しく npmjs.com にもリリースされていないという状況でした。
https://github.com/msgpack/msgpack-javascript/tree/classic
その後kawanetさんが msgpack-lite を実装したのが2015年。これが2019年現在、もっとも週間ダウンロード数の多いMessagePack for JSの実装です。
msgpack-lite ピュアJavaScript実装の速いMessagePackライブラリ - Qiita
msgpack-liteはちゃんと動いてメンテされていて stream encoder / decoder もある優秀な実装なんですが、NodeJS向けに最適化されていて、実際くだんのmsgpack-liteの紹介エントリでもBufferに大きく依存しているとあり、ブラウザサポートは「Bufferのpolyfillがあればブラウザでも動く」という水準です。用途を察するに fluentd client for NodeJS で使うことを想定しての実装であり、これはこれでベストな実装です。
ただ今回はブラウザ向けに最適化された実装がほしかったのと、ES2018 (async iteration) とTypeScriptという条件で設計したらいろいろ変わるはずだなあという思いがありました。
要件
- ES2018+ な設計と実装
- TypedArraysに依存した設計やESクラスの構文などを活用したい
AsyncIterable<Uint8Array>
をうけとるストリーミングデコーダがほしい
- ES5 (IE11) でも動く
- そうはいってもまだIE11サポートを完全には切れないので…
- TypeScriptのdownlevel compile (
target: "es5"
やdownlevelIteration: true
を利用) と polyfill (core-js) を想定する
- streaming decoder
- Fetch APIの streaming download と繋いでstreaming decodeすると、特に回線の遅い環境で効果的だと思われる
実装
ブラウザ向けに最適化といいつつ、いろいろがんばったら NodeJS v12 ではmsgpack-liteより速くなりました。@msgpack/msgpack
はDataViewに大きく依存していて、 Improving DataView performance in V8 · V8 が実装されたV8が入ったのがNodeJS v12 だからこそですね。
なお↓のベンチマークは mspgack-lite に同梱されているものを流用しています。 Buffer.from(JSON.stringify(...))
はI/Oを想定しているからで、JSON.stringifyの戻り値であるJavaScriptのstringはUTF8 encodeしてバイト列にしないとファイルに保存したりネットワークで転送したりできないため、ベンチマークの公平性という観点から入れたと思われます。MessagePackのエンコードはバイト列を生成するのでそのままI/Oに載せられるため、加工はしていません。
なおベンチマークはいまのところNodeJSでしか行ってませんが、@msgpack/msgpack
はECMA-262の機能しか使ってないのでChromeでも同じ傾向になるはずです。またMessagePack実装はmsgpack-liteとしか行ってませんが、(すくなくとも2015年時点では)msgpack-liteが最速ということだったので他の実装との比較はここでは省略しています。
Benchmark on NodeJS v12
NodeJS v12 だと encodeは Buffer.from(JSON.stringify(obj))
よりも少し速く、decodeはJSONほぼ同水準という結果になりました。
Benchmark on NodeJS/v12.1.0
operation | op | ms | op/s |
---|---|---|---|
buf = Buffer.from(JSON.stringify(obj)); | 493600 | 5000 | 98720 |
buf = JSON.stringify(obj); | 959600 | 5000 | 191920 |
obj = JSON.parse(buf); | 346100 | 5000 | 69220 |
buf = require("msgpack-lite").encode(obj); | 358300 | 5000 | 71660 |
obj = require("msgpack-lite").decode(buf); | 270400 | 5000 | 54080 |
buf = require("@msgpack/msgpack").encode(obj); | 594300 | 5000 | 118860 |
obj = require("@msgpack/msgpack").decode(buf); | 343100 | 5000 | 68620 |
Benchmark on NodeJS v10
参考までに NodejS v10 の結果も載せておきますが、見ての通りかなり遅いです。
Benchmark on NodeJS/v10.15.3
operation | op | ms | op/s |
---|---|---|---|
buf = Buffer.from(JSON.stringify(obj)); | 369500 | 5000 | 73900 |
buf = JSON.stringify(obj); | 942100 | 5000 | 188420 |
obj = JSON.parse(buf); | 356800 | 5000 | 71360 |
buf = require("msgpack-lite").encode(obj); | 283300 | 5000 | 56660 |
obj = require("msgpack-lite").decode(buf); | 211200 | 5000 | 42240 |
buf = require("@msgpack/msgpack").encode(obj); | 147000 | 5000 | 29400 |
obj = require("@msgpack/msgpack").decode(buf); | 132400 | 5000 | 26480 |
ユースケース
Kibela Web API (beta) がMessagePack (application/x-msgpack
) を扱えるので、こんな感じで decodeAsync
を使えます。
まだブラウザでのテスト(特にIE11)が十分でないので @msgpack/msgpack
のバージョンは v1.0.0 にしてませんが、ブラウザでの挙動を十分に確認したら v1.0.0
にしようと思っています。