XML, JSON and MessagePack. For JSON, three libraries are tested: Jackson, Jackson Jr and Moshi. In Protobuf side, there are two libraries used: the official implementation of Google (bêta-3.0.0) and Square Wire (2.2.0)
Processing time is the time between end of packets reception and the moment to send the final objects for display. The client requests the server for 10 files of varying numbers of subsections (50 to 500). These subsection are randomly selected so, the data received are different everytime. Each request is repeated 50 times for each number of paragraphs to get an average. The "parser" initialization time (whether for JSON, MessagePack or Protobuf) which occurs in the first round, is diluted. This choice is made to approach reality: the "parser" object is set on the first turn and is reused throughout the application usage.
Benchmark Results: ms time by the number of subsections by file
First finding already known XML is totally disqualified. For readability, we remove XML in the schema. Results:
without XML - Benchmark Time in ms by the number of subsections by file
For other formats, processing time is already superior than Protocol Buffers. For gzipped JSON, the processing cost is multiplied by 1.2 compared to JSON. The factor between JSON (with Jackson, the lowest) and Protocol Buffers is almost 2.
The conclusion is, for processing time, on text data, Protocol Buffers is clearly to his advantage.
Regarding the memory cost, we see quite easily Protocol Buffers consumes almost as little as Jackson for JSON.
At the compression of the initial data, we can see that the binary protobuf is lighter than MessagePack or JSON gzipped.
Despite these encouraging results, the establishment of a new solution may seem perilous. For Protocol Buffers, it is relatively simple. Take Android as an instance. We have the choice between the Implementation of Official Google (IOG) and Square Wire version. Beyond efficiency, compare several other points is necessary.
Two ways to generate your code: command line or pre-build. Command line:
For IOG, the latest version package is available and contains an executable:
protoc --java_out *.proto
For Wire, a jar is also available.
java -jar wire-compiler-2.2.0-jar-with-dependencies.jar --java_out =. --path_proto =. *.proto
Pre-build:
On the IOG side, it's not possible for the proto3 standard. A Gradle plugin exists but requires gradle 2.12 and includes only proto2. Wire is more effective in this regard. Square officially has a Gradle version of Wire plugin, but the repository is not updated since a year. Moreover, there is no tutorial on how to use it and it cannot be found in the official repositories of jcenter or mavenCentral. One of the forks solves these problems: the plugin Wire Gradle by Jiechic. Then, establishment is then very simple:
In project build.gradle
classpath 'com.jiechic.librairy.wire: wire-gradle-plugin: 1.0.0'
In app build.gradle:
apply plugin: 'com.squareup.wire' dependencies { compile 'com.squareup.wire: wire-runtime:2.2.0' }
Then simply place the proto files in src/main/proto. At compile time, objects will be generated in build/generated/source/proto.
Square wins. The generated files by IOG are unreadable and repulsive. The Square ones are much simpler to understand. In addition, the generated objects are also simpler to use.
In my case, only the part 'decode' of each version was used. The syntax is simple. For example, the structure is:
syntax = “proto3”; package hello; option java_package = “com.sdu.testprotoreceive”;
message Hello { string name = 1; }
IOG Version:
HelloOuterClass.Hello hello = HelloOuterClass.Hello.parseFrom(byteArray);
Wire Version:
Hello hello = hello.ADAPTER.decode(byteArray);
The objects generated by Wire is simple, they are also more natural to use.
The tests server is in Go. Objects generation from the proto3 file is:
protoc --go_out=. *.proto
The command is generic a language to another, which makes very easy to use. To interpret generated files, the library is not on the same than other languages:
go get -u github.com/golang/protobuf/protoc-gen-go go get -u github.com/golang/protobuf/proto
Example of use:
func sendProto(w http.ResponseWriter, r *http.Request) { hello := new(hello.Hello) hello.Name = “Marie” hello.Number = 32 w.Header().Set("Content-Type", "application/x-protobuf") helloCompress, err := proto.Marshal(hello) if err != nil { log.Fatal("Compress Problem (Marshal error)") } w.Write(helloCompress) }
The last tested language is C#. Again, it is easy.
public static void Main (string[] args) { WebRequest wrGETURL = WebRequest.Create( "http://xx.xx.xx.xx:8000/"); Stream streamHello = wrGETURL.GetResponse().GetResponseStream(); Hello hello = Hello.Parser.ParseFrom(ReadFully(streamHello)); Console.WriteLine (hello.Name+" "+hello.Number+" "+hello.Bool); }
Here, the function ReadFully is only to turn Stream in byte array.
Implementation: repository, Go Client, Go Server, Android Clients
A Playground on Github allows you to see an implementation more real. A Go repository contains the protobuf file and an application to generate objects in different languages. A Go server find in this repository a Go object, a Go client did the same. The Android Wire client find protobuf file and generates its objects in pre-build. The Android IOG client retrieves the jar file created by the Go repository.
Objects generation for Java, Golang and C# was done in three command lines with one protobuf file. Objects use is very similar in language tested in this article (Android, C# and Go), and looks like decode functions for JSON. For a text content, on Android client, Protobuf has several advantages such as simplicity ("build") or the gain in processing time ( "run"). Substitute JSON may even be advisable in a new project. It should nevertheless think that some tools are little or not updated (as plugin gradle by Square).
The V3 is trying to simplify use and expand the user fields, with more new languages. The binary apprehension, using new formats and JSON in place since a long time curb changement. This, I think, is the reasons which leaves Protocol Buffers little used.