From dfd4123edc1863a5b3b6d7cfabba6856c319ba5b Mon Sep 17 00:00:00 2001 From: Dmitriy Vyukov Date: Thu, 18 Sep 2014 10:13:15 -0700 Subject: encoding/gob: speedup encoding Replace typeLock with copy-on-write map using atomic.Value. benchmark old ns/op new ns/op delta BenchmarkEndToEndPipe 7722 7709 -0.17% BenchmarkEndToEndPipe-2 5114 4344 -15.06% BenchmarkEndToEndPipe-4 3192 2429 -23.90% BenchmarkEndToEndPipe-8 1833 1438 -21.55% BenchmarkEndToEndPipe-16 1332 983 -26.20% BenchmarkEndToEndPipe-32 1444 675 -53.25% BenchmarkEndToEndByteBuffer 6474 6019 -7.03% BenchmarkEndToEndByteBuffer-2 4280 2810 -34.35% BenchmarkEndToEndByteBuffer-4 2264 1774 -21.64% BenchmarkEndToEndByteBuffer-8 1275 979 -23.22% BenchmarkEndToEndByteBuffer-16 1257 753 -40.10% BenchmarkEndToEndByteBuffer-32 1342 644 -52.01% BenchmarkEndToEndArrayByteBuffer 727725 671349 -7.75% BenchmarkEndToEndArrayByteBuffer-2 394079 320473 -18.68% BenchmarkEndToEndArrayByteBuffer-4 211785 178175 -15.87% BenchmarkEndToEndArrayByteBuffer-8 141003 118857 -15.71% BenchmarkEndToEndArrayByteBuffer-16 139249 86367 -37.98% BenchmarkEndToEndArrayByteBuffer-32 144128 73454 -49.04% LGTM=r R=golang-codereviews, r CC=golang-codereviews https://golang.org/cl/147720043 --- src/encoding/gob/type.go | 76 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 23 deletions(-) (limited to 'src/encoding/gob/type.go') diff --git a/src/encoding/gob/type.go b/src/encoding/gob/type.go index cad1452795..a49b71a867 100644 --- a/src/encoding/gob/type.go +++ b/src/encoding/gob/type.go @@ -11,6 +11,7 @@ import ( "os" "reflect" "sync" + "sync/atomic" "unicode" "unicode/utf8" ) @@ -681,29 +682,51 @@ func (w *wireType) string() string { type typeInfo struct { id typeId - encoder *encEngine + encInit sync.Mutex // protects creation of encoder + encoder atomic.Value // *encEngine wire *wireType } -var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock +// typeInfoMap is an atomic pointer to map[reflect.Type]*typeInfo. +// It's updated copy-on-write. Readers just do an atomic load +// to get the current version of the map. Writers make a full copy of +// the map and atomically update the pointer to point to the new map. +// Under heavy read contention, this is significantly faster than a map +// protected by a mutex. +var typeInfoMap atomic.Value + +func lookupTypeInfo(rt reflect.Type) *typeInfo { + m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo) + return m[rt] +} -// typeLock must be held. func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { rt := ut.base if ut.externalEnc != 0 { // We want the user type, not the base type. rt = ut.user } - info, ok := typeInfoMap[rt] - if ok { + if info := lookupTypeInfo(rt); info != nil { return info, nil } - info = new(typeInfo) + return buildTypeInfo(ut, rt) +} + +// buildTypeInfo constructs the type information for the type +// and stores it in the type info map. +func buildTypeInfo(ut *userTypeInfo, rt reflect.Type) (*typeInfo, error) { + typeLock.Lock() + defer typeLock.Unlock() + + if info := lookupTypeInfo(rt); info != nil { + return info, nil + } + gt, err := getBaseType(rt.Name(), rt) if err != nil { return nil, err } - info.id = gt.id() + info := &typeInfo{id: gt.id()} if ut.externalEnc != 0 { userType, err := getType(rt.Name(), ut, rt) @@ -719,25 +742,32 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { case xText: info.wire = &wireType{TextMarshalerT: gt} } - typeInfoMap[ut.user] = info - return info, nil + rt = ut.user + } else { + t := info.id.gobType() + switch typ := rt; typ.Kind() { + case reflect.Array: + info.wire = &wireType{ArrayT: t.(*arrayType)} + case reflect.Map: + info.wire = &wireType{MapT: t.(*mapType)} + case reflect.Slice: + // []byte == []uint8 is a special case handled separately + if typ.Elem().Kind() != reflect.Uint8 { + info.wire = &wireType{SliceT: t.(*sliceType)} + } + case reflect.Struct: + info.wire = &wireType{StructT: t.(*structType)} + } } - t := info.id.gobType() - switch typ := rt; typ.Kind() { - case reflect.Array: - info.wire = &wireType{ArrayT: t.(*arrayType)} - case reflect.Map: - info.wire = &wireType{MapT: t.(*mapType)} - case reflect.Slice: - // []byte == []uint8 is a special case handled separately - if typ.Elem().Kind() != reflect.Uint8 { - info.wire = &wireType{SliceT: t.(*sliceType)} - } - case reflect.Struct: - info.wire = &wireType{StructT: t.(*structType)} + // Create new map with old contents plus new entry. + newm := make(map[reflect.Type]*typeInfo) + m, _ := typeInfoMap.Load().(map[reflect.Type]*typeInfo) + for k, v := range m { + newm[k] = v } - typeInfoMap[rt] = info + newm[rt] = info + typeInfoMap.Store(newm) return info, nil } -- cgit v1.3-5-g9baa