GradleでProtocol Bufferを使う時にはGoogleのProtobuf Plugin for GradleかWireのGradle Pluginが主流だと思いますが、それぞれのコンパイラ(protoc/wire)で生成されるJavaファイル(およびKotlinファイル)のサイズと、さらにそれをJVMバイトコードにコンパイルした時のファイルサイズを比較してみました。
きっかけはSquare社のIntroducing Wire Protocol Buffersという記事で、Protocol Bufferの実装をprotocからwireに変えたらメソッドが10,000くらい減ったと書いてあり、ファイルサイズでいうとどのくらい減るのかが気になったためです。
For the Square Register app, generated Protocol Buffer code was taking up a large chunk of our method space. By switching to Wire, we removed almost 10,000 methods from our application and gained a lot of breathing room to add new features
コンパイル対象のprotoファイル
コンパイル対象とするproroファイルは以下です。
syntax="proto3";
option java_package = "com.example.protobufsample";
// This is Person
message Person {
string first_name = 1;
string last_name = 2;
repeated string family_names = 3;
}
protoc/wireのコンパイルで生成されたJavaファイルの比較
コンパイラ | 生成ファイル | ファイルサイズ |
---|---|---|
protoc | Person.java | 20K |
Wire | Person.java | 6.4K |
Wire | Person.kt | 5.1K |
protocがGoogleのProtocol Bufferの実装で、WireはそのままWireですが、見ての通り、wireの方が3~4倍ファイルサイズが小さいですね。
ファイルサイズのみならず、可読性の面でも、Wireの方が良い印象。
JVMバイトコードのファイルサイズの比較
実際にJVMのメモリに乗る時のサイズでも一応見比べてみるために、今度はさっきのjava/kotlinファイルを、classファイルに変換してみます。
元のコンパイラ | java/kotlinファイル | classファイル | ファイルサイズ(合計) |
---|---|---|---|
protoc | Person.java | Person\$1.class, Person\$Person\$Builder.class, Person\$Person.class, Person\$PersonOrBuilder.class, Person.class | 17K |
Wire | Person.java | Person\$Builder.class, Person\$ProtoAdapter_Person.class, Person.class | 9.1K |
Wire | Person.kt | Person\$Companion\$ADAPTER\$1.class, Person\$Companion.class, Person.class | 13K |
先のjava/kotlinの時にはWireかつKotlinの組み合わせ一番小さかったですが、さらにJVMバイトコードになると、まぁ予想通り、WireかつJavaファイルの組み合わせが一番小さかったです。
なお補足ですが、JVMバイトコードへの変換はそれぞれ以下のようにやりました。
バージョンの箇所は適宜ご自分の環境に合わせてください。
// protocで生成されたjavaファイルをclassファイルに変換
javac -cp path/to/protobuf-java-3.0.0.jar Person.java
// Wireで生成されたjavaファイルをclassファイルに変換
javac -cp path/to/wire-compiler-3.6.1-jar-with-dependencies.jar Person.java
// Wireで生成されたKotlinファイルをclassファイルに変換
kotlinc -jvm-target 1.8 -cp path/to/wire-compiler-3.6.1-jar-with-dependencies.jar Person.kt
まとめ
protocやWire自体のサイズ、あるいは依存ライブラリも含めたサイズがどっちが大きいかまでは調べてないですが、少なくとも生成されるJVMバイトコードのサイズで比較する限りは、Gradleでprotoファイルをコンパイルする場合、WireでJavaファイルにコンパイルすることで場合によってはprotocより50%減できるかもしれないですね。もし可読性も重視するのでしたら、WireとKotlinの組み合わせが良きかなと思います、それでも70%ほど減らせる可能性があるので。
参考:
- https://developer.squareup.com/blog/introducing-wire-protocol-buffers/
- https://square.github.io/wire/wire_vs_protoc/
- https://search.maven.org/search?q=g:com.squareup.wire%20AND%20a:wire-compiler
- https://search.maven.org/artifact/com.google.protobuf/protobuf-java/3.15.0/bundle