Skip to content

Commit

Permalink
encoding/json: copy-on-write cacheTypeFields
Browse files Browse the repository at this point in the history
Swtich from a sync.RWMutex to atomic.Value for cacheTypeFields.

On GOARCH=386, this recovers most of the remaining performance
difference from the 1.6 release. Compared with tip on linux/386:

	name            old time/op    new time/op    delta
	CodeDecoder-40    92.8ms ± 1%    87.7ms ± 1%  -5.50%  (p=0.000 n=10+10)

	name            old speed      new speed      delta
	CodeDecoder-40  20.9MB/s ± 1%  22.1MB/s ± 1%  +5.83%  (p=0.000 n=10+10)

With more time and care, I believe more of the JSON decoder's work
could be shifted so it is done before decoding, and independent of
the number of bytes processed. Maybe someone could explore that for
Go 1.8.

For golang#16117.

Change-Id: I049655b2e5b76384a0d5f4b90e3ec7cc8d8c4340
Reviewed-on: https://go-review.googlesource.com/24472
Run-TryBot: Dmitry Vyukov <[email protected]>
Reviewed-by: Dmitry Vyukov <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
crawshaw committed Jun 27, 2016
1 parent 33fa855 commit 5f209ab
Showing 1 changed file with 13 additions and 10 deletions.
23 changes: 13 additions & 10 deletions src/encoding/json/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"unicode"
"unicode/utf8"
)
Expand Down Expand Up @@ -1231,15 +1232,14 @@ func dominantField(fields []field) (field, bool) {
}

var fieldCache struct {
sync.RWMutex
m map[reflect.Type][]field
value atomic.Value // map[reflect.Type][]field
mu sync.Mutex // used only by writers
}

// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
func cachedTypeFields(t reflect.Type) []field {
fieldCache.RLock()
f := fieldCache.m[t]
fieldCache.RUnlock()
m, _ := fieldCache.value.Load().(map[reflect.Type][]field)
f := m[t]
if f != nil {
return f
}
Expand All @@ -1251,11 +1251,14 @@ func cachedTypeFields(t reflect.Type) []field {
f = []field{}
}

fieldCache.Lock()
if fieldCache.m == nil {
fieldCache.m = map[reflect.Type][]field{}
fieldCache.mu.Lock()
m, _ = fieldCache.value.Load().(map[reflect.Type][]field)
newM := make(map[reflect.Type][]field, len(m)+1)
for k, v := range m {
newM[k] = v
}
fieldCache.m[t] = f
fieldCache.Unlock()
newM[t] = f
fieldCache.value.Store(newM)
fieldCache.mu.Unlock()
return f
}

0 comments on commit 5f209ab

Please sign in to comment.