From 7e03a132256601df109fe960e09229487f3c77eb Mon Sep 17 00:00:00 2001 From: Shawn Wang Date: Tue, 4 May 2021 08:00:10 +0800 Subject: [PATCH] Add type addrShift to enable bigger encoder/decoder cache Change-Id: I630fa501f3b96702b69a40bc0b2f79f4db2a57eb --- codec.go | 21 +++++++++++++++++---- codec_go16_test.go | 13 +++++++++++++ decode_compile_norace.go | 2 +- decode_compile_race.go | 2 +- internal/encoder/compiler.go | 2 +- internal/encoder/compiler_norace.go | 2 +- internal/encoder/compiler_race.go | 2 +- internal/runtime/type.go | 19 ++++++++++++++++--- 8 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 codec_go16_test.go diff --git a/codec.go b/codec.go index 8818bb53..e109d992 100644 --- a/codec.go +++ b/codec.go @@ -16,6 +16,7 @@ var ( cachedDecoderMap unsafe.Pointer // map[uintptr]decoder baseTypeAddr uintptr maxTypeAddr uintptr + typeAddrShift uintptr ) //go:linkname typelinks reflect.typelinks @@ -35,8 +36,10 @@ func setupCodec() error { section := sections[0] offset := offsets[0] var ( - min uintptr = uintptr(^uint(0)) - max uintptr = 0 + min uintptr = uintptr(^uint(0)) + max uintptr = 0 + isAligned64 = true + isAligned32 = true ) for i := 0; i < len(offset); i++ { typ := (*rtype)(rtypeOff(section, offset[i])) @@ -56,15 +59,25 @@ func setupCodec() error { max = addr } } + + // check every address is aligned from the base address + isAligned64 = isAligned64 && (addr-min)&63 == 0 + isAligned32 = isAligned32 && (addr-min)&31 == 0 } addrRange := max - min if addrRange == 0 { return fmt.Errorf("failed to get address range of types") } - if addrRange > maxAcceptableTypeAddrRange { + if isAligned64 { + typeAddrShift = 6 + } else if isAligned32 { + typeAddrShift = 5 + } + cacheSize := addrRange >> typeAddrShift + if cacheSize > maxAcceptableTypeAddrRange { return fmt.Errorf("too big address range %d", addrRange) } - cachedDecoder = make([]decoder, addrRange) + cachedDecoder = make([]decoder, cacheSize) baseTypeAddr = min maxTypeAddr = max return nil diff --git a/codec_go16_test.go b/codec_go16_test.go new file mode 100644 index 00000000..62cb1440 --- /dev/null +++ b/codec_go16_test.go @@ -0,0 +1,13 @@ +// +build go1.16 + +package json + +import ( + "testing" +) + +func TestTypeAddressAligned32(t *testing.T) { + if typeAddrShift != 5 { + t.Fatalf("unexpected type address shift %d, want 5", typeAddrShift) + } +} diff --git a/decode_compile_norace.go b/decode_compile_norace.go index a20f38db..4a7670ab 100644 --- a/decode_compile_norace.go +++ b/decode_compile_norace.go @@ -10,7 +10,7 @@ func decodeCompileToGetDecoder(typ *rtype) (decoder, error) { return decodeCompileToGetDecoderSlowPath(typeptr, typ) } - index := typeptr - baseTypeAddr + index := (typeptr - baseTypeAddr) >> typeAddrShift if dec := cachedDecoder[index]; dec != nil { return dec, nil } diff --git a/decode_compile_race.go b/decode_compile_race.go index fb601c4f..a70850cf 100644 --- a/decode_compile_race.go +++ b/decode_compile_race.go @@ -15,7 +15,7 @@ func decodeCompileToGetDecoder(typ *rtype) (decoder, error) { return decodeCompileToGetDecoderSlowPath(typeptr, typ) } - index := typeptr - baseTypeAddr + index := (typeptr - baseTypeAddr) >> typeAddrShift decMu.RLock() if dec := cachedDecoder[index]; dec != nil { decMu.RUnlock() diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 3038c4bd..896123d3 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -27,7 +27,7 @@ func init() { if typeAddr == nil { typeAddr = &runtime.TypeAddr{} } - cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange) + cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift) } func loadOpcodeMap() map[uintptr]*OpcodeSet { diff --git a/internal/encoder/compiler_norace.go b/internal/encoder/compiler_norace.go index 984a6fbf..acc2658b 100644 --- a/internal/encoder/compiler_norace.go +++ b/internal/encoder/compiler_norace.go @@ -12,7 +12,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { if typeptr > typeAddr.MaxTypeAddr { return compileToGetCodeSetSlowPath(typeptr) } - index := typeptr - typeAddr.BaseTypeAddr + index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift if codeSet := cachedOpcodeSets[index]; codeSet != nil { return codeSet, nil } diff --git a/internal/encoder/compiler_race.go b/internal/encoder/compiler_race.go index 7c11308d..63b7733e 100644 --- a/internal/encoder/compiler_race.go +++ b/internal/encoder/compiler_race.go @@ -15,7 +15,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) { if typeptr > typeAddr.MaxTypeAddr { return compileToGetCodeSetSlowPath(typeptr) } - index := typeptr - typeAddr.BaseTypeAddr + index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift setsMu.RLock() if codeSet := cachedOpcodeSets[index]; codeSet != nil { setsMu.RUnlock() diff --git a/internal/runtime/type.go b/internal/runtime/type.go index 88d05e16..0167cd2c 100644 --- a/internal/runtime/type.go +++ b/internal/runtime/type.go @@ -19,6 +19,7 @@ type TypeAddr struct { BaseTypeAddr uintptr MaxTypeAddr uintptr AddrRange uintptr + AddrShift uintptr } var ( @@ -49,8 +50,10 @@ func AnalyzeTypeAddr() *TypeAddr { section := sections[0] offset := offsets[0] var ( - min uintptr = uintptr(^uint(0)) - max uintptr = 0 + min uintptr = uintptr(^uint(0)) + max uintptr = 0 + isAligned64 = true + isAligned32 = true ) for i := 0; i < len(offset); i++ { typ := (*Type)(rtypeOff(section, offset[i])) @@ -70,18 +73,28 @@ func AnalyzeTypeAddr() *TypeAddr { max = addr } } + isAligned64 = isAligned64 && (addr-min)&63 == 0 + isAligned32 = isAligned32 && (addr-min)&31 == 0 } addrRange := max - min if addrRange == 0 { return nil } - if addrRange > maxAcceptableTypeAddrRange { + var addrShift uintptr + if isAligned64 { + addrShift = 6 + } else if isAligned32 { + addrShift = 5 + } + cacheSize := addrRange >> addrShift + if cacheSize > maxAcceptableTypeAddrRange { return nil } typeAddr = &TypeAddr{ BaseTypeAddr: min, MaxTypeAddr: max, AddrRange: addrRange, + AddrShift: addrShift, } return typeAddr }