mirror of
https://github.com/golang/go
synced 2024-11-02 13:42:29 +00: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
1 changed files with 66 additions and 23 deletions
|
@ -16,6 +16,7 @@ import (
|
|||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
@ -295,28 +296,10 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
|
|||
|
||||
case reflect.Struct:
|
||||
e.WriteByte('{')
|
||||
t := v.Type()
|
||||
n := v.NumField()
|
||||
first := true
|
||||
for i := 0; i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if f.PkgPath != "" {
|
||||
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) {
|
||||
for _, ef := range encodeFields(v.Type()) {
|
||||
fieldValue := v.Field(ef.i)
|
||||
if ef.omitEmpty && isEmptyValue(fieldValue) {
|
||||
continue
|
||||
}
|
||||
if first {
|
||||
|
@ -324,9 +307,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
|
|||
} else {
|
||||
e.WriteByte(',')
|
||||
}
|
||||
e.string(tag)
|
||||
e.string(ef.tag)
|
||||
e.WriteByte(':')
|
||||
e.reflectValueQuoted(fieldValue, quoted)
|
||||
e.reflectValueQuoted(fieldValue, ef.quoted)
|
||||
}
|
||||
e.WriteByte('}')
|
||||
|
||||
|
@ -470,3 +453,63 @@ func (e *encodeState) string(s string) (int, error) {
|
|||
e.WriteByte('"')
|
||||
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 a new issue