summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Crawshaw <crawshaw@golang.org>2016-06-25 10:23:30 -0400
committerDavid Crawshaw <crawshaw@golang.org>2016-06-27 15:08:12 +0000
commit5f209aba6d903688fd5d801bad3fbb5572c85a02 (patch)
tree852e4329d20b3f46a6105d591e775655f605029f
parent33fa855e6cfa906e12c624663b1010862bf4df6f (diff)
downloadgo-git-5f209aba6d903688fd5d801bad3fbb5572c85a02.tar.gz
encoding/json: copy-on-write cacheTypeFields
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 #16117. Change-Id: I049655b2e5b76384a0d5f4b90e3ec7cc8d8c4340 Reviewed-on: https://go-review.googlesource.com/24472 Run-TryBot: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Dmitry Vyukov <dvyukov@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
-rw-r--r--src/encoding/json/encode.go23
1 files changed, 13 insertions, 10 deletions
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index d864022730..6bb6de8f07 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -22,6 +22,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"unicode"
"unicode/utf8"
)
@@ -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
}
@@ -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
}