GitHub - go-json-experiment/jsonbench: JSON benchmarks to compare different Go JSON implementations (original) (raw)

JSON Benchmarks

Each of the charts below show the performance across several different JSON implementations:

The JSONv1in2 implementation replicates the JSONv1 API and behavior purely in terms of the JSONv2 implementation by setting the appropriate set of options to reproduce legacy v1 behavior.

The Go toolchain used is v1.23.5.

Based on the module proxy as of 2025-01-22, the relative popularity of each:

Note that JSONv2 deliberately dissuades users from depending on the package as it is an experiment and is subject to major breaking changes.

Benchmarks were run across various datasets:

All of the implementations other than JSONv1, JSONv1in2, JSONv2, and Sonnet make extensive use of unsafe. As such, we expect those to generally be faster, but at the cost of memory and type safety. SonicJSON goes a step even further and uses just-in-time compilation to generate machine code specialized for the Go type being marshaled or unmarshaled. Also, SonicJSON does not validate JSON strings for valid UTF-8, and so gains a notable performance boost on datasets with multi-byte Unicode. Benchmarks are performed based on the default marshal and unmarshal behavior of each package. Note that JSONv2 aims to be safe and correct by default, which may not be the most performant strategy.

JSONv2 has several semantic changes relative to JSONv1 that impacts performance:

  1. When marshaling, JSONv2 no longer sorts the keys of a Go map. This will improve performance.
  2. When marshaling or unmarshaling, JSONv2 always checks to make sure JSON object names are unique. This will hurt performance, but is more correct.
  3. When marshaling or unmarshaling, JSONv2 always shallow copies the underlying value for a Go interface and shallow copies the key and value for entries in a Go map. This is done to keep the value as addressable so that JSONv2 can call methods and functions that operate on a pointer receiver. This will hurt performance, but is more correct.
  4. When marshaling or unmarshaling, JSONv2 supports calling type-defined methods or caller-defined functions with the currentjsontext.Encoder or jsontext.Decoder. The Encoder or Decoder must contain a state machine to validate calls according to the JSON grammar. Maintaining this state will hurt performance. The JSONv1 API provides no means for obtaining the Encoder or Decoderso it never needed to explicitly maintain a state machine. Conformance to the JSON grammar is implicitly accomplished by matching against the structure of the call stack.

All of the charts are unit-less since the values are normalized relative to JSONv1, which is why JSONv1 always has a value of 1. A lower value is better (i.e., runs faster).

Benchmarks were performed on an AMD Ryzen 9 9950X.

Marshal Performance

Concrete types

Benchmark Marshal Concrete

Interface types

Benchmark Marshal Interface

RawValue types

Benchmark Marshal Rawvalue

Unmarshal Performance

Concrete types

Benchmark Unmarshal Concrete

Interface types

Benchmark Unmarshal Interface

RawValue types

Benchmark Unmarshal Rawvalue

Streaming

When reading from an io.Reader and writing to an io.Writer, a JSON implementation should not need a buffer much larger than the largest JSON token encountered within the entire JSON value. For example, marshaling and unmarshaling a [{},{},{},{},{},...]that is a gigabyte in size should not need to buffer the entire JSON array, but only enough to buffer each individual { or }. An implementation with true streaming support will use a fixed amount of memory regardless of the total size of the JSON value.

The following implementations have true streaming support:

Implementation Marshal Unmarshal
JSONv1
JSONv1in2
JSONv2 ✔️ ✔️
JSONIterator ✔️
SegmentJSON
GoJSON
SonicJSON
SonnetJSON

See TestStreaming for more information.

Correctness

A package may be fast, but it must still be correct and realiable.

--- FAIL: TestRoundtrip/TwitterStatus/Interface/GoJSON/MarshalWrite (0.04s)  
Marshal error: encoder: opcode  has not been implemented  
--- FAIL: TestRoundtrip/GolangSource/Interface/GoJSON/MarshalWrite (0.16s)  
Marshal error: opcode SliceEnd has not been implemented  
--- FAIL: TestRoundtrip/GolangSource/Interface/GoJSON/Marshal  
Marshal error: invalid character ',' after object key  
--- FAIL: TestRoundtrip/TwitterStatus/Interface/GoJSON/MarshalWrite (0.01s)  
panic: runtime error: slice bounds out of range [10409248:74824]  
goroutine 111 [running]:  
testing.tRunner.func1.2({0xa3c220, 0xc002cec228})  
  go1.23.5/src/testing/testing.go:1632 +0x230  
testing.tRunner.func1()  
  go1.23.5/src/testing/testing.go:1635 +0x35e  
panic({0xa3c220?, 0xc002cec228?})  
  go1.23.5/src/runtime/panic.go:785 +0x132  
github.com/goccy/go-json/internal/encoder/vm.Run(0xc00308a000, {0xc003088000?, 0x0?, 0x400?}, 0xc000ce9880?)  
  github.com/goccy/go-json@v0.10.4/internal/encoder/vm/vm.go:440 +0x25ae5  
github.com/goccy/go-json.encodeRunCode(0xc00308a000?, {0xc003088000?, 0x0?, 0xc0001a53c0?}, 0xc002fb5d38?)  
  github.com/goccy/go-json@v0.10.4/encode.go:310 +0x56  
github.com/goccy/go-json.encode(0xc00308a000, {0x9be140, 0xc002022fb0})  
  github.com/goccy/go-json@v0.10.4/encode.go:235 +0x205  
github.com/goccy/go-json.(*Encoder).encodeWithOption(0xc003093e58, 0xc00308a000, {0x9be140, 0xc002022fb0}, {0x0, 0x0, 0xc002fb5e90?})  
  github.com/goccy/go-json@v0.10.4/encode.go:77 +0x129  
github.com/goccy/go-json.(*Encoder).EncodeWithOption(0xc002fb5e58, {0x9be140, 0xc002022fb0}, {0x0, 0x0, 0x0})  
  github.com/goccy/go-json@v0.10.4/encode.go:42 +0x89  
github.com/goccy/go-json.(*Encoder).Encode(...)  
  github.com/goccy/go-json@v0.10.4/encode.go:34  
jsonbench.init.func19({0xb55e20?, 0xc003086000?}, {0x9be140?, 0xc002022fb0?})  
  github.com/go-json-experiment/jsonbench/bench_test.go:121 +0x69  
jsonbench.TestRoundtrip.func3(0xc001fd0ea0)  
  github.com/go-json-experiment/jsonbench/bench_test.go:175 +0x135  
testing.tRunner(0xc001fd0ea0, 0xc0004efa80)  
  go1.23.5/src/testing/testing.go:1690 +0xf4  
created by testing.(*T).Run in goroutine 9  
  go1.23.5/src/testing/testing.go:1743 +0x390  
exit status 2  
--- FAIL: TestRoundtrip/TwitterStatus/Interface/GoJSON/MarshalWrite (0.03s)  
panic: runtime error: invalid memory address or nil pointer dereference  
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x859f9e]  
goroutine 114 [running]:  
testing.tRunner.func1.2({0x9f1ee0, 0xf8a7b0})  
  go1.23.5/src/testing/testing.go:1632 +0x230  
testing.tRunner.func1()  
  go1.23.5/src/testing/testing.go:1635 +0x35e  
panic({0x9f1ee0?, 0xf8a7b0?})  
  go1.23.5/src/runtime/panic.go:785 +0x132  
github.com/goccy/go-json/internal/encoder/vm.Run(0xc003f32000, {0xc003f30000?, 0x0?, 0x400?}, 0xc003f3a000?)  
  github.com/goccy/go-json@v0.10.4/internal/encoder/vm/vm.go:26 +0x5e  
github.com/goccy/go-json.encodeRunCode(0xc003f32000?, {0xc003f30000?, 0xc0026301c0?, 0xc00069cc40?}, 0xc003f21d38?)  
  github.com/goccy/go-json@v0.10.4/encode.go:310 +0x56  
github.com/goccy/go-json.encode(0xc003f32000, {0x9be140, 0xc00203e580})  
  github.com/goccy/go-json@v0.10.4/encode.go:235 +0x205  
github.com/goccy/go-json.(*Encoder).encodeWithOption(0xc003f43e58, 0xc003f32000, {0x9be140, 0xc00203e580}, {0x0, 0x0, 0xc003f21e90?})  
  github.com/goccy/go-json@v0.10.4/encode.go:77 +0x129  
github.com/goccy/go-json.(*Encoder).EncodeWithOption(0xc003f21e58, {0x9be140, 0xc00203e580}, {0x0, 0x0, 0x0})  
  github.com/goccy/go-json@v0.10.4/encode.go:42 +0x89  
github.com/goccy/go-json.(*Encoder).Encode(...)  
  github.com/goccy/go-json@v0.10.4/encode.go:34  
jsonbench.init.func19({0xb55e20?, 0xc003f28000?}, {0x9be140?, 0xc00203e580?})  
  github.com/go-json-experiment/jsonbench/bench_test.go:121 +0x69  
jsonbench.TestRoundtrip.func3(0xc00262e1a0)  
  github.com/go-json-experiment/jsonbench/bench_test.go:175 +0x135  
testing.tRunner(0xc00262e1a0, 0xc000538c00)  
  go1.23.5/src/testing/testing.go:1690 +0xf4  
created by testing.(*T).Run in goroutine 65  
  go1.23.5/src/testing/testing.go:1743 +0x390  
exit status 2  
unexpected fault address 0x49af0034  
fatal error: fault  
[signal SIGSEGV: segmentation violation code=0x1 addr=0x49af0034 pc=0x859ffa]  
goroutine 27 gp=0xc0000d7880 m=29 mp=0xc006a0a008 [running]:  
runtime.throw({0xa79779?, 0x4871aa?})  
  go1.23.5/src/runtime/panic.go:1067 +0x48 fp=0xc0030b5908 sp=0xc0030b58d8 pc=0x473928  
runtime.sigpanic()  
  go1.23.5/src/runtime/signal_unix.go:931 +0x26c fp=0xc0030b5968 sp=0xc0030b5908 pc=0x47552c  
github.com/goccy/go-json/internal/encoder/vm.Run(0xc0030ae000, {0xc00012fc00?, 0x0?, 0x400?}, 0xc003610000?)  
  github.com/goccy/go-json@v0.10.4/internal/encoder/vm/vm.go:32 +0xba fp=0xc0030b7cb8 sp=0xc0030b5968 pc=0x859ffa  
github.com/goccy/go-json.encodeRunCode(0xc0030ae000?, {0xc00012fc00?, 0xc0000d7880?, 0xc000638a00?}, 0xc003088d38?)  
  github.com/goccy/go-json@v0.10.4/encode.go:310 +0x56 fp=0xc0030b7cf0 sp=0xc0030b7cb8 pc=0x881056  
github.com/goccy/go-json.encode(0xc0030ae000, {0x9be140, 0xc000e90360})  
  github.com/goccy/go-json@v0.10.4/encode.go:235 +0x205 fp=0xc0030b7d70 sp=0xc0030b7cf0 pc=0x880be5  
github.com/goccy/go-json.(*Encoder).encodeWithOption(0xc0030b7e58, 0xc0030ae000, {0x9be140, 0xc000e90360}, {0x0, 0x0, 0xc0004aae90?})  
  github.com/goccy/go-json@v0.10.4/encode.go:77 +0x129 fp=0xc0030b7dc8 sp=0xc0030b7d70 pc=0x880729  
github.com/goccy/go-json.(*Encoder).EncodeWithOption(0xc003088e58, {0x9be140, 0xc000e90360}, {0x0, 0x0, 0x0})  
  github.com/goccy/go-json@v0.10.4/encode.go:42 +0x89 fp=0xc0030b7e28 sp=0xc0030b7dc8 pc=0x880569  
github.com/goccy/go-json.(*Encoder).Encode(...)  
  github.com/goccy/go-json@v0.10.4/encode.go:34  
jsonbench.init.func19({0xb55e20?, 0xc00308aae0?}, {0x9be140?, 0xc000e90360?})  
  github.com/go-json-experiment/jsonbench/bench_test.go:121 +0x69 fp=0xc0030b7ea0 sp=0xc0030b7e28 pc=0x991b89  
jsonbench.TestRoundtrip.func3(0xc0004c5ba0)  
  github.com/go-json-experiment/jsonbench/bench_test.go:175 +0x135 fp=0xc0030b7f70 sp=0xc0030b7ea0 pc=0x992f95  
testing.tRunner(0xc0004c5ba0, 0xc000277a00)  
  go1.23.5/src/testing/testing.go:1690 +0xf4 fp=0xc0030b7fc0 sp=0xc0030b7f70 pc=0x535954  
testing.(*T).Run.gowrap1()  
  go1.23.5/src/testing/testing.go:1743 +0x25 fp=0xc0030b7fe0 sp=0xc0030b7fc0 pc=0x536945  
runtime.goexit({})  
  go1.23.5/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0030b7fe8 sp=0xc0030b7fe0 pc=0x47b8e1  
created by testing.(*T).Run in goroutine 63  
  go1.23.5/src/testing/testing.go:1743 +0x390  
runtime: marked free object in span 0x7f784c8c0910, elemsize=896 freeindex=3 (bad use of unsafe.Pointer? try -d=checkptr)  
0xc0037b0000 alloc unmarked  
0xc0037b0380 alloc marked  
0xc0037b0700 alloc marked  
0xc0037b0a80 free  marked   zombie  
0x000000c0037b0a80:  0x0000000000a5ae00  0x0000000000000017  
0x000000c0037b0a90:  0x000000c0037b0b00  0x000000c0037b0ce0  
0x000000c0037b0aa0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ab0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ac0:  0x00000000009ed520  0x0000000000000000  
0x000000c0037b0ad0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ae0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0af0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0b00:  0x0000001000000013  0x000000c0037b0b78  
0x000000c0037b0b10:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0b20:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0b30:  0x0000000000000000  0x00000000009cc4c0  
0x000000c0037b0b40:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0b50:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0b60:  0x0000000000000001  0x0000000000000000  
0x000000c0037b0b70:  0x0000000000000000  0x0000000000000008  
0x000000c0037b0b80:  0x000000c0037b0bf0  0x000000c0037b0ce0  
0x000000c0037b0b90:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ba0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0bb0:  0x00000000009e1f40  0x0000000000000000  
0x000000c0037b0bc0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0bd0:  0x0000000000000000  0x0000000000000002  
0x000000c0037b0be0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0bf0:  0x0000002000000001  0x000000c0037b0c68  
0x000000c0037b0c00:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0c10:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0c20:  0x0004000000000000  0x00000000009e1f40  
0x000000c0037b0c30:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0c40:  0x0000000800000000  0x0000000000000001  
0x000000c0037b0c50:  0x0000000000000003  0x0000000000000000  
0x000000c0037b0c60:  0x0000000000000000  0x0000000000000007  
0x000000c0037b0c70:  0x000000c0037b0b00  0x000000c0037b0ce0  
0x000000c0037b0c80:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0c90:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ca0:  0x00000000009cc4c0  0x0000000000000000  
0x000000c0037b0cb0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0cc0:  0x0000000000000000  0x0000000000000004  
0x000000c0037b0cd0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0ce0:  0x0000000000000009  0x000000c0037b0d58  
0x000000c0037b0cf0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d00:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d10:  0x0000000000000000  0x00000000009ed520  
0x000000c0037b0d20:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d30:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d40:  0x0000000000000005  0x0000000000000000  
0x000000c0037b0d50:  0x0000000000000000  0x000000400000000d  
0x000000c0037b0d60:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d70:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d80:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0d90:  0x00000000009ed520  0x0000000000000000  
0x000000c0037b0da0:  0x0000000000000000  0x0000005000000048  
0x000000c0037b0db0:  0x0000000000000000  0x0000000000000006  
0x000000c0037b0dc0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0dd0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0de0:  0x0000000000000000  0x0000000000000000  
0x000000c0037b0df0:  0x0000000000000000  0x0000000000000000  
0xc0037b0e00 alloc marked  
0xc0037b1180 alloc marked  
0xc0037b1500 free  unmarked  
0xc0037b1880 free  unmarked  
0xc0037b1c00 free  unmarked  
fatal error: found pointer to free object  

Use of unsafe

While it is possible to use unsafe correctly, it is difficult to do so as you lose the benefits of type safety. Even experienced Go programmers have introduced bugs with unsafe that could lead to memory corruption, remote code execution, or worse.

The following table shows whether each implementation uses unsafe:

Implementation Uses unsafe
JSONv1 🛡️ no
JSONv1in2 🛡️ no
JSONv2 🛡️ no
JSONIterator 💣 yes
SegmentJSON 💣 yes
GoJSON 💣 yes
SonicJSON 💣 yes
SonnetJSON 🛡️ no

Notes:

Our test suite was unable to trigger any memory corruption bugs in JSONIterator, SegmentJSON, or SonicJSON, which do use unsafe. Similarly, our test quite was unable to trigger any memory corruption bugs in JSONv1, JSONv1in2, JSONv2, and SonnetJSON, which do not use unsafe, but could still have race conditions. The inability to trigger bugs does not imply that there are no bugs. Caveat emptor.

UTF-8 Validation

According to RFC 8259, section 8.1, a JSON value must be encoded using UTF-8.

The following table shows how each implementation handles invalid UTF-8:

Implementation Marshal Unmarshal
JSONv1 ⚠️ replaced ⚠️ replaced
JSONv1in2 ⚠️ replaced ⚠️ replaced
JSONv2 ✔️ rejected ✔️ rejected
JSONIterator ⚠️ replaced ❌ ignored
SegmentJSON ⚠️ replaced ⚠️ replaced
GoJSON ⚠️ replaced ❌ ignored
SonicJSON ❌ ignored ❌ ignored
SonnetJSON ⚠️ replaced ⚠️ replaced

Notes:

See TestValidateUTF8 for more information.

Duplicate Object Names

RFC 8259, section 4specifies that handling of a JSON object with duplicate names results in undefined behavior where compliant parsers may use the first member, the last member, all the members, or report an error.RFC 7493, section 2.3specifies that JSON objects must not have duplicate names. Rejecting duplicate object names is more correct, but incurs a performance cost verifying this property.

The following table shows how each implementation handles duplicate object names:

Implementation Marshal Unmarshal
JSONv1 ❌ allowed ❌ allowed
JSONv1in2 ❌ allowed ❌ allowed
JSONv2 ✔️ rejected ✔️ rejected
JSONIterator ❌ allowed ❌ allowed
SegmentJSON ❌ allowed ❌ allowed
GoJSON ❌ allowed ❌ allowed
SonicJSON ❌ allowed ❌ allowed
SonnetJSON ❌ allowed ❌ allowed

See TestDuplicateNames for more information.

Parsing Test Suite

"Parsing JSON is a Minefield 💣"(posted 2016-10-26) performed one of the first thorough comparisons of JSON parsers and their behavior on various edge-cases. At the time, RFC 7159was the authoritative standard, but has since been superseded byRFC 8259. Consequently, the expected results of some of the test cases from the article were changed to be more compliant with RFC 8259.

The following table shows the number of test case failures for each implementation when tested against RFC 8259:

Implementation String Number Object Array Other
JSONv1 ❌ 10x ✔️ ✔️ ✔️ ✔️
JSONv1in2 ❌ 10x ✔️ ✔️ ✔️ ✔️
JSONv2 ✔️ ✔️ ✔️ ✔️ ✔️
JSONIterator ❌ 10x ❌ 4x ✔️ ✔️ ✔️
SegmentJSON ❌ 10x ✔️ ✔️ ✔️ ✔️
GoJSON ❌ 30x ❌ 52x ❌ 20x ❌ 17x ❌ 10x
SonicJSON ❌ 28x ✔️ ✔️ ❌ 1x ✔️
SonnetJSON ❌ 10x ✔️ ✔️ ✔️ ✔️

RFC 7493is compatible with RFC 8259 in that it makes strict decisions about behavior that RFC 8259 leaves undefined. In particular, it rejects escaped surrogate pairs that are invalid and rejects JSON object with duplicate names.

The following table shows additional test case failures for each implementation when tested against RFC 7493:

Implementation String Number Object Array Other
JSONv1 ❌ 9x ✔️ ❌ 3x ✔️ ✔️
JSONv1in2 ❌ 9x ✔️ ❌ 3x ✔️ ✔️
JSONv2 ✔️ ✔️ ✔️ ✔️ ✔️
JSONIterator ❌ 9x ✔️ ❌ 3x ✔️ ✔️
SegmentJSON ❌ 9x ✔️ ❌ 3x ✔️ ✔️
GoJSON ❌ 9x ✔️ ❌ 3x ✔️ ✔️
SonicJSON ❌ 9x ✔️ ❌ 3x ✔️ ✔️
SonnetJSON ❌ 9x ✔️ ❌ 3x ✔️ ✔️

See TestParseSuite for more information.

MarshalJSON Validation

A JSON implementation should not trust that the output of a MarshalJSON method is valid JSON nor formatted in the same way as surrounding JSON. Consequently, it should parse and reformat the JSON output to be consistent.

The following table shows which implementations validate MarshalJSON output:

Implementation Validates
JSONv1 ✔️ yes
JSONv1in2 ✔️ yes
JSONv2 ✔️ yes
JSONIterator ❌ no
SegmentJSON ✔️ yes
GoJSON ✔️ yes
SonicJSON ✔️ yes
SonnetJSON ✔️ yes

See TestValidateMarshalJSON for more information.

Deterministic Map Ordering

RFC 8259specifies that JSON objects are an "unordered collection". Thus, a compliant JSON marshaler need not serialize Go maps entries in any particular order.

The JSONv1 implementation historically sorted keys, which consequently set the precedence for other JSON implementations to do likewise. The JSONv2 implementation no longer sorts keys for better performance and because it does not violate any specified facet of correctness.

The following table shows which implementations deterministically marshal maps:

Implementation Deterministic
JSONv1 ✔️ yes
JSONv1in2 ✔️ yes
JSONv2 ❌ no
JSONIterator ❌ no
SegmentJSON ✔️ yes
GoJSON ✔️ yes
SonicJSON ❌ no
SonnetJSON ❌ no

See TestMapDeterminism for more information.

Observable Changes With Unmarshal Errors

Implementations differ regarding how much of the output value is modified when an unmarshaling error is encountered.

There are generally two reasonable behaviors:

  1. Make no mutating changes to the output if the input is invalid.
  2. Make as many changes as possible up until the input becomes invalid.

The following table shows what changes are observable if the input is invalid:

Implementation Observable Changes
JSONv1 ✔️ none
JSONv1in2 ✔️ none
JSONv2 ⚠️ all
JSONIterator ⚠️ all
SegmentJSON ❌ some
GoJSON ❌ some
SonicJSON ⚠️ all
SonnetJSON ❌ some

See TestUnmarshalErrors for more information.

Binary Size

For use in embedded or mobile applications, a small binary size is a priority. The following table shows the binary sizes of each JSON implementation for a simple Go program that just links in json.Marshal and json.Unmarshal. These were built with GOOS=linux and GOARCH=amd64.

Implementation Size
JSONv1 2.511 MiB
JSONv1in2 3.460 MiB
JSONv2 3.394 MiB
JSONIterator 3.354 MiB
SegmentJSON 3.035 MiB
GoJSON 3.720 MiB
SonicJSON 7.100 MiB
SonnetJSON 2.479 MiB

See TestBinarySize for more information.