All Benchmarks

Go Benchmark

JSON Encoding Libraries

encoding/json vs goccy/go-json vs json-iterator vs sonic.

jsonencodingmarshalunmarshalsonicserialization

encoding/json is famously one of the slower parts of the Go standard library, and a whole ecosystem of drop-in replacements has grown around it. This benchmark marshals and unmarshals a typical small API struct with the standard library, github.com/goccy/go-json, github.com/json-iterator/go, and ByteDance's github.com/bytedance/sonic (JIT + SIMD accelerated). Unmarshal is where the standard library loses the most ground — its reflection-heavy decoder is several times slower than the alternatives. Before adding a dependency, remember the other side of the trade: encoding/json is maintained by the Go team, has no unsafe tricks, and its successor encoding/json/v2 (still experimental behind GOEXPERIMENT=jsonv2) closes much of this gap.

linux/amd64AMD Ryzen 9 9950X3D 16-Core Processorbenchmarks/json-alternatives
Compare atCPUs

1 CPU

Marshal

Fastest

Goccy Json

94.93 ns/op

Slowest

Encoding Json

180.22 ns/op · 1.9x slower

Unmarshal

Fastest

Goccy Json

105.53 ns/op

Slowest

Encoding Json

820.38 ns/op · 7.8x slower

32 CPUs

Marshal

Fastest

Goccy Json

96.68 ns/op

Slowest

Encoding Json

195.55 ns/op · 2.0x slower

Unmarshal

Fastest

Goccy Json

112.66 ns/op

Slowest

Encoding Json

839.93 ns/op · 7.5x slower

Performance Comparison (lower is better)
CPU:

Goccy Json #

Fastest (Marshal)Fastest (Unmarshal)

github.com/goccy/go-json, an API-compatible replacement that compiles per-type opcode sequences instead of interpreting reflection on every call. Pure Go (no JIT or assembly), works on every platform, and typically the fastest of the pure-Go options — especially for decoding.

Performance (lower is better)
CPU:
// sampleUser returns the payload encoded and decoded by every implementation.
func sampleUser() User {
	return User{
		ID:     42,
		Name:   "Gopher",
		Email:  "gopher@example.com",
		Age:    15,
		Active: true,
		Tags:   []string{"go", "benchmark", "json"},
	}
}

func BenchmarkGoccyJson_marshal(b *testing.B) {
	user := sampleUser()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sinkBytes, _ = goccy.Marshal(user)
	}
}

func BenchmarkGoccyJson_unmarshal(b *testing.B) {
	data, _ := goccy.Marshal(sampleUser())
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = goccy.Unmarshal(data, &sinkUser)
	}
}
1 CPU

Marshal

1.9xfaster(90%)thanEncoding Json
1.6xfaster(56%)thanJson Iterator
1.2xfaster(15%)thanSonic

Unmarshal

7.8xfaster(677%)thanEncoding Json
1.7xfaster(70%)thanJson Iterator
1.5xfaster(54%)thanSonic
32 CPUs

Marshal

2xfaster(102%)thanEncoding Json
1.5xfaster(46%)thanJson Iterator
1.1xfaster(6%)thanSonic

Unmarshal

7.5xfaster(646%)thanEncoding Json
1.6xfaster(61%)thanJson Iterator
1.4xfaster(42%)thanSonic

Sonic #

ByteDance's github.com/bytedance/sonic, which JIT-compiles codecs at runtime and uses SIMD instructions for scanning. Extremely fast, but the heavy machinery has costs: amd64/arm64 only, unsafe under the hood, and new Go versions need explicit support from the library before you can upgrade.

Performance (lower is better)
CPU:
// sampleUser returns the payload encoded and decoded by every implementation.
func sampleUser() User {
	return User{
		ID:     42,
		Name:   "Gopher",
		Email:  "gopher@example.com",
		Age:    15,
		Active: true,
		Tags:   []string{"go", "benchmark", "json"},
	}
}

func BenchmarkSonic_marshal(b *testing.B) {
	user := sampleUser()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sinkBytes, _ = sonic.Marshal(user)
	}
}

func BenchmarkSonic_unmarshal(b *testing.B) {
	data, _ := sonic.Marshal(sampleUser())
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = sonic.Unmarshal(data, &sinkUser)
	}
}
1 CPU

Marshal

1.6xfaster(65%)thanEncoding Json
1.2xslower(15%)thanGoccy Json
1.4xfaster(35%)thanJson Iterator

Unmarshal

5xfaster(404%)thanEncoding Json
1.5xslower(54%)thanGoccy Json
1.1xfaster(10%)thanJson Iterator
32 CPUs

Marshal

1.9xfaster(91%)thanEncoding Json
1.1xslower(6%)thanGoccy Json
1.4xfaster(38%)thanJson Iterator

Unmarshal

5.2xfaster(424%)thanEncoding Json
1.4xslower(42%)thanGoccy Json
1.1xfaster(13%)thanJson Iterator

Json Iterator #

github.com/json-iterator/go with ConfigCompatibleWithStandardLibrary, an iterator-style decoder that was the go-to encoding/json replacement for years. Still noticeably faster than the standard library at decoding, but development has largely stalled and newer libraries have overtaken it.

Performance (lower is better)
CPU:
// sampleUser returns the payload encoded and decoded by every implementation.
func sampleUser() User {
	return User{
		ID:     42,
		Name:   "Gopher",
		Email:  "gopher@example.com",
		Age:    15,
		Active: true,
		Tags:   []string{"go", "benchmark", "json"},
	}
}

func BenchmarkJsonIterator_marshal(b *testing.B) {
	api := jsoniter.ConfigCompatibleWithStandardLibrary
	user := sampleUser()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sinkBytes, _ = api.Marshal(user)
	}
}

func BenchmarkJsonIterator_unmarshal(b *testing.B) {
	api := jsoniter.ConfigCompatibleWithStandardLibrary
	data, _ := api.Marshal(sampleUser())
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = api.Unmarshal(data, &sinkUser)
	}
}
1 CPU

Marshal

1.2xfaster(22%)thanEncoding Json
1.6xslower(56%)thanGoccy Json
1.4xslower(35%)thanSonic

Unmarshal

4.6xfaster(358%)thanEncoding Json
1.7xslower(70%)thanGoccy Json
1.1xslower(10%)thanSonic
32 CPUs

Marshal

1.4xfaster(38%)thanEncoding Json
1.5xslower(46%)thanGoccy Json
1.4xslower(38%)thanSonic

Unmarshal

4.6xfaster(363%)thanEncoding Json
1.6xslower(61%)thanGoccy Json
1.1xslower(13%)thanSonic

Encoding Json #

Slowest (Marshal)Slowest (Unmarshal)

The standard library's encoding/json. Marshalling uses cached reflection-based encoders and holds up reasonably well; unmarshalling walks the input with a reflection-driven decoder, which is where most of its reputation for slowness comes from. Zero dependencies, battle-tested, and the safe default.

Performance (lower is better)
CPU:
// sampleUser returns the payload encoded and decoded by every implementation.
func sampleUser() User {
	return User{
		ID:     42,
		Name:   "Gopher",
		Email:  "gopher@example.com",
		Age:    15,
		Active: true,
		Tags:   []string{"go", "benchmark", "json"},
	}
}

func BenchmarkEncodingJson_marshal(b *testing.B) {
	user := sampleUser()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sinkBytes, _ = json.Marshal(user)
	}
}

func BenchmarkEncodingJson_unmarshal(b *testing.B) {
	data, _ := json.Marshal(sampleUser())
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = json.Unmarshal(data, &sinkUser)
	}
}
1 CPU

Marshal

1.9xslower(90%)thanGoccy Json
1.2xslower(22%)thanJson Iterator
1.6xslower(65%)thanSonic

Unmarshal

7.8xslower(677%)thanGoccy Json
4.6xslower(358%)thanJson Iterator
5xslower(404%)thanSonic
32 CPUs

Marshal

2xslower(102%)thanGoccy Json
1.4xslower(38%)thanJson Iterator
1.9xslower(91%)thanSonic

Unmarshal

7.5xslower(646%)thanGoccy Json
4.6xslower(363%)thanJson Iterator
5.2xslower(424%)thanSonic

Contributors