Icon

Charles Crete

Flutter JSON vs Protocol Buffer: Benefits and performance

May 19, 2020

As discussed in my last post on Flutter JSON decoding performance, we saw that parsing JSON on the main thread is usually the right choice for decoding data.

Today, we'll be looking at Protocol Buffers, Google's data serialization format. Protocol Buffers serves the same purpose as JSON, with the following differences:

  • It is structured. You create .proto files which define the schema of the data, and generate code for encoding/decoding
  • It is a binary format. This means that it doesn't need to encode to a string (like JSON). For example, "123" can encode to 1 byte instead of 3.

Protocol Buffers claims to be faster to encode/decode and provide a small message size. Let's test this out in the context of Flutter.

The data

At first, I wanted to reuse the JSON messages from my previous post for direct comparison, but since Protocol Buffer schemas are stricter, some of the messages were impossible to convert to schemas (dissimilar objects in an array).

Instead, I've created 2 schemas (Short & Long), one which would be your typical API response, and one with more data (with some other types).

syntax = "proto3";

message Link {
    int64 id = 1;
    string text = 2;
    string url = 3;
}

message Person {
    int64 id = 1;
    string name = 2;
}

message Short {
    int64 id = 1;
    string firstName = 2;
    string lastName = 3;

    repeated Link links = 4;
    repeated Person friends = 5;
}

message Long {
    Short short1 = 1;
    Short short2 = 2;
    Short short3 = 3;

    bytes raw = 4;

    string text = 5;
    repeated float numbers = 6;

    enum ContentType {
        IMAGE = 0;
        VIDEO = 1;
        DOCUMENT = 2;
    }

    repeated ContentType contentTypes = 7;
}

We'll be using the following JSON representations for this version (with bytes as base64):

  • Short: 377 bytes minified (~619 characters)
  • Long: 6168 bytes minified (~7414 characters)

Next, I created the Protocol Buffer messages with the same data (uploaded as base64):

  • Short: 196 bytes (48.0% reduction)
  • Long: 4332 bytes (29.8% reduction)

Great. Now we'll set up our parsing. We'll be reusing code from the last post, and will be doing all parsing on the main thread. We'll also be using the minified JSON.

void parseJson() {
  json.decode(jsonToParse);
}

void parseProtocolBuffer() {
  Short.fromBuffer(shortProtocolBuffer);
  // or
  Long.fromBuffer(longProtocolBuffer);
}

We'll be using the same devices as our last post:

  • OnePlus 8 Pro using a 865 (2020), in release mode
  • Pixel 3XL using a 855 (2018), in release mode
  • Local emulator running on a 9700K (3 dedicated CPUs), in debug mode (evaluated), because I'm curious

...and the same versions:

  • Flutter: 1.19.0-1.0.pre
  • Dart: 2.9.0-7.0.dev

To measure, we'll use the rough average of the output burst. This gives us a pretty good idea of the relative performance and excludes outliers.

Results

Device Short JSON Long JSON Short Protobuf Long Protobuf
OnePlus 8 Pro (865) ~151 000 ~15 500 ~175 000 ~19 200
Pixel 3XL (855) ~99 000 ~9 200 ~115 000 ~12 000
Desktop (9700K) ~313 000 ~32 200 ~339 000 ~35 800

Here, we see a clear performance win. We see a 15-23% win on the OnePlus and a 16-30% win on the Pixel.

Conclusion

Protocol Buffers definitely seem like a better option over JSON. Let's look over it again:

  • ✓ Smaller size, with a 29.8-48.0% reduction in size compared in our examples.
  • ✓ Better performance, with an average 21% speedup in parsing.
  • ✓ Schema definition, allowing for static typing and easier backward-compatibility
  • ✗ Harder to use, setting up the testing took me almost an hour. This is usually a one-time cost though.

My recommendation would be to use Protocol Buffers if you need (or want) better parsing performance and can accept the added complexity of Protobufs. You can learn more about them here, along with it's Dart tutorial.

About me

I am Charles Crete, author of the redux_persist, flutter_linkify, and flutter_super_state (and more!) libraries for Flutter/Dart.

Follow me on Twitter!