mirror of
https://github.com/golang/go
synced 2024-11-19 02:04:42 -07:00
encoding/json: optimize Marshal for maps
Optimize marshaling of maps by using slices.SortFunc. This drops an unnecessary field from reflectWithString, which also reduces the cost of each swap operation. benchmark old ns/op new ns/op delta BenchmarkMarshalMap-10 228 139 -39.24% benchmark old allocs new allocs delta BenchmarkMarshalMap-10 11 8 -27.27% benchmark old bytes new bytes delta BenchmarkMarshalMap-10 432 232 -46.30% Change-Id: Ic2ba7a1590863c7536305c6f6536372b26ec9b0c Reviewed-on: https://go-review.googlesource.com/c/go/+/515176 Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Daniel Martí <mvdan@mvdan.cc> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: qiulaidongfeng <2645477756@qq.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
dd38ade8d1
commit
4c5dac7202
@ -246,6 +246,22 @@ func BenchmarkMarshalBytesError(b *testing.B) {
|
|||||||
b.Run("4096", benchMarshalBytesError(4096))
|
b.Run("4096", benchMarshalBytesError(4096))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkMarshalMap(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
m := map[string]int{
|
||||||
|
"key3": 3,
|
||||||
|
"key2": 2,
|
||||||
|
"key1": 1,
|
||||||
|
}
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
if _, err := Marshal(m); err != nil {
|
||||||
|
b.Fatal("Marshal:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkCodeDecoder(b *testing.B) {
|
func BenchmarkCodeDecoder(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
if codeJSON == nil {
|
if codeJSON == nil {
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -739,16 +740,20 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
|
|||||||
e.WriteByte('{')
|
e.WriteByte('{')
|
||||||
|
|
||||||
// Extract and sort the keys.
|
// Extract and sort the keys.
|
||||||
sv := make([]reflectWithString, v.Len())
|
var (
|
||||||
mi := v.MapRange()
|
sv = make([]reflectWithString, v.Len())
|
||||||
|
mi = v.MapRange()
|
||||||
|
err error
|
||||||
|
)
|
||||||
for i := 0; mi.Next(); i++ {
|
for i := 0; mi.Next(); i++ {
|
||||||
sv[i].k = mi.Key()
|
if sv[i].ks, err = resolveKeyName(mi.Key()); err != nil {
|
||||||
sv[i].v = mi.Value()
|
|
||||||
if err := sv[i].resolve(); err != nil {
|
|
||||||
e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
|
e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))
|
||||||
}
|
}
|
||||||
|
sv[i].v = mi.Value()
|
||||||
}
|
}
|
||||||
sort.Slice(sv, func(i, j int) bool { return sv[i].ks < sv[j].ks })
|
slices.SortFunc(sv, func(i, j reflectWithString) int {
|
||||||
|
return strings.Compare(i.ks, j.ks)
|
||||||
|
})
|
||||||
|
|
||||||
for i, kv := range sv {
|
for i, kv := range sv {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
@ -927,31 +932,26 @@ func typeByIndex(t reflect.Type, index []int) reflect.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type reflectWithString struct {
|
type reflectWithString struct {
|
||||||
k reflect.Value
|
|
||||||
v reflect.Value
|
v reflect.Value
|
||||||
ks string
|
ks string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *reflectWithString) resolve() error {
|
func resolveKeyName(k reflect.Value) (string, error) {
|
||||||
if w.k.Kind() == reflect.String {
|
if k.Kind() == reflect.String {
|
||||||
w.ks = w.k.String()
|
return k.String(), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if tm, ok := w.k.Interface().(encoding.TextMarshaler); ok {
|
if tm, ok := k.Interface().(encoding.TextMarshaler); ok {
|
||||||
if w.k.Kind() == reflect.Pointer && w.k.IsNil() {
|
if k.Kind() == reflect.Pointer && k.IsNil() {
|
||||||
return nil
|
return "", nil
|
||||||
}
|
}
|
||||||
buf, err := tm.MarshalText()
|
buf, err := tm.MarshalText()
|
||||||
w.ks = string(buf)
|
return string(buf), err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
switch w.k.Kind() {
|
switch k.Kind() {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
w.ks = strconv.FormatInt(w.k.Int(), 10)
|
return strconv.FormatInt(k.Int(), 10), nil
|
||||||
return nil
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
w.ks = strconv.FormatUint(w.k.Uint(), 10)
|
return strconv.FormatUint(k.Uint(), 10), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
panic("unexpected map key type")
|
panic("unexpected map key type")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user