Skip to content

encoding/json/v2: performance regression when umarshalling in []any #77642

Description

@gaaf

Unmarshaling into a []any with preset typed elements becomes 25% slower with v1 when run under the jsonv2 experiment. In addition, it does 50% more allocations. Switching to purely v2 is slightly faster but triples the amount of allocations.

My use case for this scenario is parsing the array variant of JSON-RPC 2.0 params fields, which may be encoded as a JSON object or array.

Go version 1.26.0.

goos: linux
goarch: amd64
cpu: AMD Ryzen 5 5600G with Radeon Graphics         
         │    v1.txt    │            v1-wrap-v2.txt            │               v2.txt               │
         │    sec/op    │    sec/op     vs base                │   sec/op     vs base               │
Array-12   1064.5n ± 1%   1335.0n ± 1%  +25.41% (p=0.000 n=10)   959.3n ± 1%  -9.88% (p=0.000 n=10)

         │   v1.txt    │           v1-wrap-v2.txt           │               v2.txt                │
         │    B/op     │    B/op     vs base                │    B/op      vs base                │
Array-12   176.00 ± 0%   48.00 ± 0%  -72.73% (p=0.000 n=10)   224.00 ± 0%  +27.27% (p=0.000 n=10)

         │   v1.txt   │           v1-wrap-v2.txt           │                v2.txt                │
         │ allocs/op  │ allocs/op   vs base                │  allocs/op   vs base                 │
Array-12   4.000 ± 0%   6.000 ± 0%  +50.00% (p=0.000 n=10)   11.000 ± 0%  +175.00% (p=0.000 n=10)

Test case:

package jsonrpc2_test

import (
	"encoding/json"
	// json "encoding/json/v2"
	"reflect"
	"testing"
)

type Params struct {
	Name   string
	Count  uint64
	Index  int32
	Price  float64
	Active bool
	Tags   []string
}

var JSONArray = []byte(
	`[
		"example",
		1844674407370955161,
		42,
		1234.567,
		true,
		["a","b","c"]
	]`,
)

func BenchmarkArray(b *testing.B) {
	paramsType := reflect.TypeOf((*Params)(nil)).Elem()
	fieldCount := paramsType.NumField()
	// Set slice element's types to Params element's types
	args := make([]any, fieldCount)
	for i := 0; i < fieldCount; i++ {
		args[i] = reflect.New(paramsType.Field(i).Type).Interface()
	}

	for i := 0; i < b.N; i++ {
		json.Unmarshal(JSONArray, &args)
	}
}

For purely v2 testing, the json import line is changed to json "encoding/json/v2".

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Performance

    Type

    No type

    Projects

    Status
    Post-proposal

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions