エキサイト株式会社エンジニアの佐々木です。弊社では結構なリクエストがあるので、キャッシュをいたるところに使用しております。キャッシュサーバ等に一時的にデータを避難するために、シリアライズ・デシリアライズ処理が必須となります。一般的には、Protobuf、MessagePack、Avroなどが有名ですが、JVMのみのやりとりであればKryo(クライオ)というものが選択肢に入ります。こちらのご紹介になります。
前提
$ java --version openjdk 17.0.2 2022-01-18 OpenJDK Runtime Environment Temurin-17.0.2+8 (build 17.0.2+8) OpenJDK 64-Bit Server VM Temurin-17.0.2+8 (build 17.0.2+8, mixed mode) $ ./gradlew --version ------------------------------------------------------------ Gradle 7.6.1 ------------------------------------------------------------ Build time: 2023-02-24 13:54:42 UTC Revision: 3905fe8ac072bbd925c70ddbddddf4463341f4b4 Kotlin: 1.7.10 Groovy: 3.0.13 Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021 JVM: 17.0.2 (Eclipse Adoptium 17.0.2+8) OS: Mac OS X 12.5 aarch64
Gradle Dependencies
dependencies { implementation 'com.esotericsoftware:kryo:5.5.0' }
コード
Kryoでシリアライズするコードは下記になります。
private static byte[] serializeByKryo(SampleData data) { Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); // class情報が設定されていなくてもシリアライズ・デシリアライズができるようにする(パフォーマンスは落ちる) try ( ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Output output = new Output(outputStream) ) { kryo.writeClassAndObject(output, data); // シリアライズ化 return output.toBytes(); } catch (IOException e) { throw new RuntimeException(e); } } private static byte[] deserializeByKryo(byte[] data) { Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); // class情報が設定されていなくてもシリアライズ・デシリアライズができるようにする(パフォーマンスは落ちる) try (ByteArrayInputStream inputStream = new ByteArrayInputStream(data); Input input = new Input(inputStream)) { return (SampleData) kryo.readClassAndObject(input); } catch (IOException e) { throw new RuntimeException(e); } } ## JMHによるベンチマークテスト Benchmark Mode Cnt Score Error Units RedisMainTest.JacksonSerialize thrpt 5 39046.433 ± 7585.141 ops/s RedisMainTest.kryoSerialize thrpt 5 3448556.879 ± 56029.843 ops/s
簡単に書くと上記でKryoを使用できていることになります。 Outputクラス
はAutoClosableインターフェースを実装していますので、try-with-resourcesの中で宣言すれば、終了時に自動的にクローズされメモリリークがなくなります。こういうところは大変便利かなと思います。
結果
JDKシリアライズ、Jackson(JSON)で比較した結果だと下記のような結果になります。
## シリアライズ後のサイズ Kryo binary size: 211 JDK binary size: 630 Jackson(json) binary size: 214
JDKシリアライズとは3倍くらいサイズが異なります。現時点でJDK標準のSerializerを使用しているところはあまりないと思いますが、切り替えることでネットワークコストやシリアライズ・デシリアライズにかかるコストを軽減できるかと思います。今回の結果だと、Jacksonを使用したJSONへのシリアライズの結果だとバイナリサイズでの差分はほとんどありませんでした。
まとめ
Kryoは、Java環境のみで使用可能ですが簡単にコンパクトなシリアライズが可能なライブラリとなります。Javaで利用できる様々なシリアライザ比較の参考サイトをリンクしておきます。
Home · eishay/jvm-serializers Wiki · GitHub
Kryoは、他のシリアライザと比較しても結構成績がよくアプリケーションのパフォーマンス向上にも寄与してくれそうです。しかし、Kryoは単体ではスレッドセーフではないので、実装する際には注意が必要です。(別記事で対応方法を投稿します)
最後に
エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。
募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com