New JSON encoder for Haskell that is up to 3x faster than "aeson"

Words: nikita-volkov - - 13:02 16-10-2020

Minimalistic library for encoding JSON directly to strict bytestring.

The library focuses on 2 aspects: simplicity and performance.

The API consists of just a few functions and

achieves performance that gets up to 3 times better than that of "aeson"

in typical use-cases.

In cases where we deal with really large documents (60MB) the performance

of "aeson" becomes more comparable.

Following are the benchmark results comparing the performance

of encoding typical documents using this library and "aeson".

The numbers after the slash identify the amount of objects in

the rendered JSON.

"lazy-aeson" stands for "aeson" producing a lazy bytestring,

otherwise it's strict.

Here is the listing of the data sizes of produced documents by the amounts of objects:

The benchmark suite is bundled with the package.

Such performance is achieved due to the approach taken to the process of building a bytestring. Unlike "aeson", this library doesn't use the builder distributed with the "bytestring" package, instead it uses a custom solution which produces a bytestring in two steps: first it counts how many bytes the rendering of data will occupy then it allocates a buffer of that exact size and renders directly into it. As the benchmarks show, at least for the purpose of rendering JSON this approach turns out to be faster than manipulations on temporary buffers which the builder from "bytestring" does.

This approach opens doors to optimizations otherwise inaccessible. E.g., we can efficiently count how many bytes a Text value encoded as JSON string literal will occupy, then render it into its final destination in one pass. We can efficiently count how many bytes a decimal encoding of an integer will occupy, and also render it in one pass despite the rendering of integers needing to be done in reverse direction and requiring a second pass of reversing the bytes in alternative solutions.

With all those observations some general concepts have emerged and have been extracted as the lower-level "ptr-poker" package, which focuses on the problem of populating pointers.

The quality of the library is ensured with a test property in which a random JSON tree is generated, then rendered using "jsonifier", then parsed using "aeson" and compared to the original.

Following is a complete program that shows how you can render

JSON from your domain model.

A compilable version of this demo comes bundled with the package as the "demo" test-suite.