All Benchmarks

Go Benchmark

JSON Struct vs Map

Comparing JSON serialization performance between typed structs and generic maps (map[string]any).

jsonserializationstructmapencoding

When working with JSON in Go, you can either marshal and unmarshal into strongly typed structs or loosely typed maps (map[string]any). This benchmark compares the performance of both methods, covering both serialization (marshal) and deserialization (unmarshal). Strongly typed structs offer known type bounds for the reflections package, lowering runtime overhead and allocations.

linux/amd64AMD Ryzen 9 9950X3D 16-Core Processorbenchmarks/json-struct-vs-map
Compare atCPUs

1 CPU

Marshal

Fastest

Struct Payload

101.25 ns/op

Slowest

Map Payload

408.64 ns/op · 4.0x slower

Unmarshal

Fastest

Struct Payload

458.97 ns/op

Slowest

Map Payload

638.06 ns/op

32 CPUs

Marshal

Fastest

Struct Payload

105.97 ns/op

Slowest

Map Payload

408.23 ns/op · 3.9x slower

Unmarshal

Fastest

Struct Payload

455.57 ns/op

Slowest

Map Payload

661.54 ns/op

Performance Comparison (lower is better)
CPU:

Struct Payload #

Fastest (Marshal)Fastest (Unmarshal)

Serializes and deserializes JSON using a strictly typed struct. This is the idiomatic way to handle JSON in Go. Because the fields and types are known in advance, it significantly reduces allocations and execution time while providing compile-time type safety.

Performance (lower is better)
CPU:
const JsonData = `{"id":12345,"name":"John Doe","email":"john.doe@example.com","is_active":true}`

func getStructData() StructPayload {
	return StructPayload{
		ID:       12345,
		Name:     "John Doe",
		Email:    "john.doe@example.com",
		IsActive: true,
	}
}

func BenchmarkStructPayload_marshal(b *testing.B) {
	data := getStructData()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		structMarshalSink, _ = json.Marshal(data)
	}
}

func BenchmarkStructPayload_unmarshal(b *testing.B) {
	data := []byte(JsonData)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = json.Unmarshal(data, &structUnmarshalSink)
	}
}
1 CPU

Marshal

4xfaster(304%)thanMap Payload

Unmarshal

1.4xfaster(39%)thanMap Payload
32 CPUs

Marshal

3.9xfaster(285%)thanMap Payload

Unmarshal

1.5xfaster(45%)thanMap Payload

Map Payload #

Slowest (Marshal)Slowest (Unmarshal)

Serializes and deserializes JSON using a dynamic map[string]any. While this approach is flexible and can handle arbitrary JSON shapes, it comes with a high overhead in execution time and heap allocations, as the standard library has to allocate map entries and parse types dynamically.

Performance (lower is better)
CPU:
const JsonData = `{"id":12345,"name":"John Doe","email":"john.doe@example.com","is_active":true}`

func getMapData() map[string]any {
	return map[string]any{
		"id":        12345,
		"name":      "John Doe",
		"email":     "john.doe@example.com",
		"is_active": true,
	}
}

func BenchmarkMapPayload_marshal(b *testing.B) {
	data := getMapData()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mapMarshalSink, _ = json.Marshal(data)
	}
}

func BenchmarkMapPayload_unmarshal(b *testing.B) {
	data := []byte(JsonData)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = json.Unmarshal(data, &mapUnmarshalSink)
	}
}
1 CPU

Marshal

4xslower(304%)thanStruct Payload

Unmarshal

1.4xslower(39%)thanStruct Payload
32 CPUs

Marshal

3.9xslower(285%)thanStruct Payload

Unmarshal

1.5xslower(45%)thanStruct Payload

Contributors