mirror of
https://github.com/golang/go
synced 2024-11-19 23:04:40 -07:00
json: speed up encoding, caching reflect calls
Before json.BenchmarkCodeEncoder 10 181232100 ns/op 10.71 MB/s json.BenchmarkCodeMarshal 10 184578000 ns/op 10.51 MB/s After: json.BenchmarkCodeEncoder 10 146444000 ns/op 13.25 MB/s json.BenchmarkCodeMarshal 10 151428500 ns/op 12.81 MB/s R=rsc, r CC=golang-dev https://golang.org/cl/5416046
This commit is contained in:
parent
f3aa54e30d
commit
6c9f466273
@ -16,6 +16,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
@ -295,28 +296,10 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
|
|||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
e.WriteByte('{')
|
e.WriteByte('{')
|
||||||
t := v.Type()
|
|
||||||
n := v.NumField()
|
|
||||||
first := true
|
first := true
|
||||||
for i := 0; i < n; i++ {
|
for _, ef := range encodeFields(v.Type()) {
|
||||||
f := t.Field(i)
|
fieldValue := v.Field(ef.i)
|
||||||
if f.PkgPath != "" {
|
if ef.omitEmpty && isEmptyValue(fieldValue) {
|
||||||
continue
|
|
||||||
}
|
|
||||||
tag, omitEmpty, quoted := f.Name, false, false
|
|
||||||
if tv := f.Tag.Get("json"); tv != "" {
|
|
||||||
if tv == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name, opts := parseTag(tv)
|
|
||||||
if isValidTag(name) {
|
|
||||||
tag = name
|
|
||||||
}
|
|
||||||
omitEmpty = opts.Contains("omitempty")
|
|
||||||
quoted = opts.Contains("string")
|
|
||||||
}
|
|
||||||
fieldValue := v.Field(i)
|
|
||||||
if omitEmpty && isEmptyValue(fieldValue) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if first {
|
if first {
|
||||||
@ -324,9 +307,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
|
|||||||
} else {
|
} else {
|
||||||
e.WriteByte(',')
|
e.WriteByte(',')
|
||||||
}
|
}
|
||||||
e.string(tag)
|
e.string(ef.tag)
|
||||||
e.WriteByte(':')
|
e.WriteByte(':')
|
||||||
e.reflectValueQuoted(fieldValue, quoted)
|
e.reflectValueQuoted(fieldValue, ef.quoted)
|
||||||
}
|
}
|
||||||
e.WriteByte('}')
|
e.WriteByte('}')
|
||||||
|
|
||||||
@ -470,3 +453,63 @@ func (e *encodeState) string(s string) (int, error) {
|
|||||||
e.WriteByte('"')
|
e.WriteByte('"')
|
||||||
return e.Len() - len0, nil
|
return e.Len() - len0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encodeField contains information about how to encode a field of a
|
||||||
|
// struct.
|
||||||
|
type encodeField struct {
|
||||||
|
i int // field index in struct
|
||||||
|
tag string
|
||||||
|
quoted bool
|
||||||
|
omitEmpty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
typeCacheLock sync.RWMutex
|
||||||
|
encodeFieldsCache = make(map[reflect.Type][]encodeField)
|
||||||
|
)
|
||||||
|
|
||||||
|
// encodeFields returns a slice of encodeField for a given
|
||||||
|
// struct type.
|
||||||
|
func encodeFields(t reflect.Type) []encodeField {
|
||||||
|
typeCacheLock.RLock()
|
||||||
|
fs, ok := encodeFieldsCache[t]
|
||||||
|
typeCacheLock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
typeCacheLock.Lock()
|
||||||
|
defer typeCacheLock.Unlock()
|
||||||
|
fs, ok = encodeFieldsCache[t]
|
||||||
|
if ok {
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.Zero(t)
|
||||||
|
n := v.NumField()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
f := t.Field(i)
|
||||||
|
if f.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var ef encodeField
|
||||||
|
ef.i = i
|
||||||
|
ef.tag = f.Name
|
||||||
|
|
||||||
|
tv := f.Tag.Get("json")
|
||||||
|
if tv != "" {
|
||||||
|
if tv == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, opts := parseTag(tv)
|
||||||
|
if isValidTag(name) {
|
||||||
|
ef.tag = name
|
||||||
|
}
|
||||||
|
ef.omitEmpty = opts.Contains("omitempty")
|
||||||
|
ef.quoted = opts.Contains("string")
|
||||||
|
}
|
||||||
|
fs = append(fs, ef)
|
||||||
|
}
|
||||||
|
encodeFieldsCache[t] = fs
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user