- Zig 96.1%
- C++ 2.5%
- Rust 0.7%
- Python 0.6%
|
Ezequiel Ramis
b1f489a96b
- update docs
- remove unnecessary reading logic - rename schema data types |
||
|---|---|---|
| .github | update mlugg/setup-zig | |
| bench | - update docs | |
| build | - update docs | |
| docs/assets | voy escribiendo el readme | |
| scripts | voy escribiendo el readme | |
| src | - update docs | |
| tests | - update docs | |
| tools | feat: migrate to 0.16 | |
| .envrc | first commit | |
| .gitignore | feat: migrate to 0.16 | |
| build.zig | port schema benchmarks and CI | |
| build.zig.zon | feat: migrate to 0.16 | |
| flake.lock | feat: migrate to 0.16 | |
| flake.nix | feat: migrate to 0.16 | |
| LICENSE | agrego readme y licencia | |
| README.md | - update docs | |
zimdjson
JSON is everywhere on the Internet. Servers spend a lot of time parsing it. We need a fresh approach.
Welcome to zimdjson: a high-performance JSON parser that takes advantage of SIMD vector instructions, based on the paper Parsing Gigabytes of JSON per Second.
The majority of the source code is based on the C++ implementation https://github.com/simdjson/simdjson with the addition of some fundamental features like:
- Streaming support which can handle arbitrarily large documents with O(1) of memory usage.
- An ergonomic, Serde-like deserialization interface thanks to Zig's compile-time reflection. See Reflection-based JSON.
- More efficient memory usage.
Getting started
Install the zimdjson library by running the following command in your project root:
zig fetch --save git+https://github.com/ezequielramis/zimdjson#0.2.0
Then write the following in your build.zig:
constzimdjson=b.dependency("zimdjson",.{});exe.root_module.addImport("zimdjson",zimdjson.module("zimdjson"));As an example, download a sample file called twitter.json.
Then execute the following:
conststd=@import("std");constzimdjson=@import("zimdjson");pubfnmain(init:std.process.Init)!void{constgpa=init.gpa;constio=init.io;varparser=zimdjson.ondemand.StreamParser(.default).init;deferparser.deinit(io,gpa);constfile=trystd.Io.Dir.cwd().openFile(io,"twitter.json",.{});deferfile.close(io);varread_buf:[4096]u8=undefined;varreader=file.reader(io,&read_buf);constdocument=tryparser.parseFromReader(io,gpa,&reader.interface);constmetadata_count=trydocument.at("search_metadata").at("count").asUnsigned();std.debug.print("{} results.",.{metadata_count});}> zig build run
100 results.
To see how the streaming parser above handles multi-gigabyte JSON documents with minimal memory usage, download one of these dumps or play it with a file of your choice.
Requirements
- Zig 0.16.0
- Linux, Windows or macOS target
- CPU with SIMD capabilities
Missing targets can be added by contributing.
Documentation
The most recent documentation can be found in https://zimdjson.ramis.ar.
Reflection-based JSON
Although the provided interfaces are simple enough, it is expected to have unnecessary boilerplate when deserializing lots of data structures. Thank to Zig's compile-time reflection, we can eliminate it:
conststd=@import("std");constzimdjson=@import("zimdjson");constFilm=struct{name:[]constu8,year:u32,characters:[]const[]constu8,// we could also use std.ArrayListUnmanaged([]const u8)};pubfnmain(init:std.process.Init)!void{constgpa=init.gpa;varparser=zimdjson.ondemand.FullParser(.default).init;deferparser.deinit(init.io,gpa);constjson=\\{\\ "name": "Esperando la carroza",\\ "year": 1985,\\ "characters": [\\ "Mamá Cora",\\ "Antonio",\\ "Sergio",\\ "Emilia",\\ "Jorge"\\ ]\\};constdocument=tryparser.parseFromSlice(gpa,json);constfilm=trydocument.as(Film,gpa,.{});deferfilm.deinit();trystd.testing.expectEqualDeep(Film{.name="Esperando la carroza",.year=1985,.characters=&.{"Mamá Cora","Antonio","Sergio","Emilia","Jorge",},},film.value,);}This is just a simple example, but this way of deserializing is as powerful as Serde, so there is a lot of more features we can use, such as:
- Deserializing data structures from the Zig Standard Library.
- Renaming fields.
- Using different union representations.
- Custom handling unknown fields.
To see all available options it offers checkout its reference.
To see all supported Zig Standard Library's data structures checkout this list.
To see how it can be really used checkout the test suite for more examples.
Performance
Note
As a rule of thumb, do not trust any benchmark — always verify it yourself. There may be biases that favor a particular candidate, including mine.
The following picture represents parsing speed in GB/s of similar tasks presented in the paper On-Demand JSON: A Better Way to Parse
Documents?, where the first three tasks iterate over twitter.json and the others iterate over a 626MB JSON file called systemsPopulated.json from these dumps.
Ok, it seems the benchmark got borked but it is not, because of how cache works on small files and how the streaming parser happily ended finding out the tweet in the middle of the file.
Let's get rid of that task to see better the other results.
The following picture corresponds to a second simple benchmark, representing parsing speed in GB/s for near-complete parsing of the twitter.json file with reflection-based parsers (serde_json, std.json).
Note: If you look closely, you'll notice that "zimdjson (On-Demand, Unordered)" is the slowest of all. This is, unfortunately, a behaviour that also occurs with simdjson when object keys are unordered. If you do not know the order, it can be mitigated by using an schema. Thanks to the glaze library author for pointing this out.
All benchmarks were run on a 3.30GHz Intel Skylake processor.