浏览代码

codec: do not pool decNaked anymore - make it smaller and part of Decoder

This requires that we eliminate caching reflect.Value for each field of decNaked
and we find performant ways (especially in unsafe mode) to get a reflect.Value
for each decNaked field.
Ugorji Nwoke 7 年之前
父节点
当前提交
23876e7a59
共有 9 个文件被更改,包括 165 次插入87 次删除
  1. 1 1
      codec/binc.go
  2. 1 1
      codec/cbor.go
  3. 63 74
      codec/decode.go
  4. 7 8
      codec/helper.go
  5. 23 0
      codec/helper_not_unsafe.go
  6. 67 0
      codec/helper_unsafe.go
  7. 1 1
      codec/json.go
  8. 1 1
      codec/msgpack.go
  9. 1 1
      codec/simple.go

+ 1 - 1
codec/binc.go

@@ -877,7 +877,7 @@ func (d *bincDecDriver) DecodeNaked() {
 		d.readNextBd()
 	}
 
-	n := d.d.n
+	n := d.d.naked()
 	var decodeFurther bool
 
 	switch d.vd {

+ 1 - 1
codec/cbor.go

@@ -633,7 +633,7 @@ func (d *cborDecDriver) DecodeNaked() {
 		d.readNextBd()
 	}
 
-	n := d.d.n
+	n := d.d.naked()
 	var decodeFurther bool
 
 	switch d.bd {

+ 63 - 74
codec/decode.go

@@ -11,7 +11,6 @@ import (
 	"reflect"
 	"runtime"
 	"strconv"
-	"sync"
 	"time"
 )
 
@@ -24,8 +23,8 @@ const (
 const (
 	decDefMaxDepth         = 1024 // maximum depth
 	decDefSliceCap         = 8
-	decDefChanCap          = 64                      // should be large, as cap cannot be expanded
-	decScratchByteArrayLen = cacheLineSize + (8 * 2) // - (8 * 1)
+	decDefChanCap          = 64            // should be large, as cap cannot be expanded
+	decScratchByteArrayLen = cacheLineSize // + (8 * 2) // - (8 * 1)
 )
 
 var (
@@ -1302,19 +1301,19 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 	case valueTypeNil:
 		// no-op
 	case valueTypeInt:
-		rvn = n.ri
+		rvn = n.ri()
 	case valueTypeUint:
-		rvn = n.ru
+		rvn = n.ru()
 	case valueTypeFloat:
-		rvn = n.rf
+		rvn = n.rf()
 	case valueTypeBool:
-		rvn = n.rb
+		rvn = n.rb()
 	case valueTypeString, valueTypeSymbol:
-		rvn = n.rs
+		rvn = n.rs()
 	case valueTypeBytes:
-		rvn = n.rl
+		rvn = n.rl()
 	case valueTypeTime:
-		rvn = n.rt
+		rvn = n.rt()
 	default:
 		panicv.errorf("kInterfaceNaked: unexpected valueType: %d", n.v)
 	}
@@ -1928,69 +1927,55 @@ type decNaked struct {
 	b bool
 
 	// state
-	v              valueType
-	li, lm, ln, ls uint8
-	inited         bool
-	_              bool // padding
+	v valueType
+	_ [6]bool // padding
 
-	ru, ri, rf, rl, rs, rb, rt reflect.Value // mapping to the primitives above
-
-	_ [3]uint64 // padding
-}
-
-func (n *decNaked) init() {
-	if n.inited {
-		return
-	}
-	n.ru = reflect.ValueOf(&n.u).Elem()
-	n.ri = reflect.ValueOf(&n.i).Elem()
-	n.rf = reflect.ValueOf(&n.f).Elem()
-	n.rl = reflect.ValueOf(&n.l).Elem()
-	n.rs = reflect.ValueOf(&n.s).Elem()
-	n.rt = reflect.ValueOf(&n.t).Elem()
-	n.rb = reflect.ValueOf(&n.b).Elem()
-
-	n.inited = true
-	// n.rr[] = reflect.ValueOf(&n.)
-}
-
-func (n *decNaked) reset() {
-	if n == nil {
-		return
-	}
-	n.li, n.lm, n.ln, n.ls = 0, 0, 0, 0
-}
+	// ru, ri, rf, rl, rs, rb, rt reflect.Value // mapping to the primitives above
+	//
+	// _ [3]uint64 // padding
+}
+
+// func (n *decNaked) init() {
+// 	n.ru = reflect.ValueOf(&n.u).Elem()
+// 	n.ri = reflect.ValueOf(&n.i).Elem()
+// 	n.rf = reflect.ValueOf(&n.f).Elem()
+// 	n.rl = reflect.ValueOf(&n.l).Elem()
+// 	n.rs = reflect.ValueOf(&n.s).Elem()
+// 	n.rt = reflect.ValueOf(&n.t).Elem()
+// 	n.rb = reflect.ValueOf(&n.b).Elem()
+// 	// n.rr[] = reflect.ValueOf(&n.)
+// }
 
-type decNakedPooler struct {
-	n   *decNaked
-	nsp *sync.Pool
-}
+// type decNakedPooler struct {
+// 	n   *decNaked
+// 	nsp *sync.Pool
+// }
 
-// naked must be called before each call to .DecodeNaked, as they will use it.
-func (d *decNakedPooler) naked() *decNaked {
-	if d.n == nil {
-		// consider one of:
-		//   - get from sync.Pool  (if GC is frequent, there's no value here)
-		//   - new alloc           (safest. only init'ed if it a naked decode will be done)
-		//   - field in Decoder    (makes the Decoder struct very big)
-		// To support using a decoder where a DecodeNaked is not needed,
-		// we prefer #1 or #2.
-		// d.n = new(decNaked) // &d.nv // new(decNaked) // grab from a sync.Pool
-		// d.n.init()
-		var v interface{}
-		d.nsp, v = pool.decNaked()
-		d.n = v.(*decNaked)
-	}
-	return d.n
-}
+// // naked must be called before each call to .DecodeNaked, as they will use it.
+// func (d *decNakedPooler) naked() *decNaked {
+// 	if d.n == nil {
+// 		// consider one of:
+// 		//   - get from sync.Pool  (if GC is frequent, there's no value here)
+// 		//   - new alloc           (safest. only init'ed if it a naked decode will be done)
+// 		//   - field in Decoder    (makes the Decoder struct very big)
+// 		// To support using a decoder where a DecodeNaked is not needed,
+// 		// we prefer #1 or #2.
+// 		// d.n = new(decNaked) // &d.nv // new(decNaked) // grab from a sync.Pool
+// 		// d.n.init()
+// 		var v interface{}
+// 		d.nsp, v = pool.decNaked()
+// 		d.n = v.(*decNaked)
+// 	}
+// 	return d.n
+// }
 
-func (d *decNakedPooler) end() {
-	if d.n != nil {
-		// if n != nil, then nsp != nil (they are always set together)
-		d.nsp.Put(d.n)
-		d.n, d.nsp = nil, nil
-	}
-}
+// func (d *decNakedPooler) end() {
+// 	if d.n != nil {
+// 		// if n != nil, then nsp != nil (they are always set together)
+// 		d.nsp.Put(d.n)
+// 		d.n, d.nsp = nil, nil
+// 	}
+// }
 
 // type rtid2rv struct {
 // 	rtid uintptr
@@ -2294,14 +2279,15 @@ type Decoder struct {
 	mtid uintptr
 	stid uintptr
 
-	decNakedPooler
-
-	h  *BasicHandle
 	hh Handle
+	h  *BasicHandle
+
 	// ---- cpu cache line boundary?
 	decReaderSwitch
 
 	// ---- cpu cache line boundary?
+	n decNaked
+
 	// cr containerStateRecv
 	err error
 
@@ -2371,7 +2357,6 @@ func newDecoder(h Handle) *Decoder {
 
 func (d *Decoder) resetCommon() {
 	// d.r = &d.decReaderSwitch
-	d.n.reset()
 	d.d.reset()
 	d.err = nil
 	d.calls = 0
@@ -2437,6 +2422,10 @@ func (d *Decoder) ResetBytes(in []byte) {
 	d.resetCommon()
 }
 
+func (d *Decoder) naked() *decNaked {
+	return &d.n
+}
+
 // Decode decodes the stream from reader and stores the result in the
 // value pointed to by v. v cannot be a nil pointer. v can also be
 // a reflect.Value of a pointer.
@@ -2576,7 +2565,7 @@ func (d *Decoder) Release() {
 		d.bi.buf = nil
 		d.bi.bytesBufPooler.end()
 	}
-	d.decNakedPooler.end()
+	// d.decNakedPooler.end()
 }
 
 // // this is not a smart swallow, as it allocates objects and does unnecessary work.

+ 7 - 8
codec/helper.go

@@ -2374,12 +2374,11 @@ type pooler struct {
 	strRv8, strRv16, strRv32, strRv64, strRv128 sync.Pool // for stringRV
 
 	// lifetime-scoped pooled resources
-	dn                                 sync.Pool // for decNaked
+	// dn                                 sync.Pool // for decNaked
 	buf1k, buf2k, buf4k, buf8k, buf16k sync.Pool // for [N]byte
 }
 
 func (p *pooler) init() {
-	// function-scoped pooled resources
 	p.tiload.New = func() interface{} { return new(typeInfoLoadArray) }
 
 	p.strRv8.New = func() interface{} { return new([8]sfiRv) }
@@ -2388,14 +2387,14 @@ func (p *pooler) init() {
 	p.strRv64.New = func() interface{} { return new([64]sfiRv) }
 	p.strRv128.New = func() interface{} { return new([128]sfiRv) }
 
-	// lifetime-scoped pooled resources
+	// p.dn.New = func() interface{} { x := new(decNaked); x.init(); return x }
+
 	p.buf1k.New = func() interface{} { return new([1 * 1024]byte) }
 	p.buf2k.New = func() interface{} { return new([2 * 1024]byte) }
 	p.buf4k.New = func() interface{} { return new([4 * 1024]byte) }
 	p.buf8k.New = func() interface{} { return new([8 * 1024]byte) }
 	p.buf16k.New = func() interface{} { return new([16 * 1024]byte) }
 
-	p.dn.New = func() interface{} { x := new(decNaked); x.init(); return x }
 }
 
 func (p *pooler) sfiRv8() (sp *sync.Pool, v interface{}) {
@@ -2429,14 +2428,14 @@ func (p *pooler) bytes8k() (sp *sync.Pool, v interface{}) {
 func (p *pooler) bytes16k() (sp *sync.Pool, v interface{}) {
 	return &p.buf16k, p.buf16k.Get()
 }
-
-func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {
-	return &p.dn, p.dn.Get()
-}
 func (p *pooler) tiLoad() (sp *sync.Pool, v interface{}) {
 	return &p.tiload, p.tiload.Get()
 }
 
+// func (p *pooler) decNaked() (sp *sync.Pool, v interface{}) {
+// 	return &p.dn, p.dn.Get()
+// }
+
 // func (p *pooler) decNaked() (v *decNaked, f func(*decNaked) ) {
 // 	sp := &(p.dn)
 // 	vv := sp.Get()

+ 23 - 0
codec/helper_not_unsafe.go

@@ -141,6 +141,29 @@ func (x *atomicRtidFnSlice) store(p []codecRtidFn) {
 	x.v.Store(p)
 }
 
+// --------------------------
+func (n *decNaked) ru() reflect.Value {
+	return reflect.ValueOf(&n.u).Elem()
+}
+func (n *decNaked) ri() reflect.Value {
+	return reflect.ValueOf(&n.i).Elem()
+}
+func (n *decNaked) rf() reflect.Value {
+	return reflect.ValueOf(&n.f).Elem()
+}
+func (n *decNaked) rl() reflect.Value {
+	return reflect.ValueOf(&n.l).Elem()
+}
+func (n *decNaked) rs() reflect.Value {
+	return reflect.ValueOf(&n.s).Elem()
+}
+func (n *decNaked) rt() reflect.Value {
+	return reflect.ValueOf(&n.t).Elem()
+}
+func (n *decNaked) rb() reflect.Value {
+	return reflect.ValueOf(&n.b).Elem()
+}
+
 // --------------------------
 func (d *Decoder) raw(f *codecFnInfo, rv reflect.Value) {
 	rv.SetBytes(d.rawBytes())

+ 67 - 0
codec/helper_unsafe.go

@@ -237,6 +237,73 @@ func (x *atomicClsErr) store(p clsErr) {
 	atomic.StorePointer(&x.v, unsafe.Pointer(&p))
 }
 
+// --------------------------
+
+// to create a reflect.Value for each member field of decNaked,
+// we first create a global decNaked, and create reflect.Value
+// for them all.
+// This way, we have the flags and type in the reflect.Value.
+// Then, when a reflect.Value is called, we just copy it,
+// update the ptr to the decNaked's, and return it.
+
+type unsafeDecNakedWrapper struct {
+	decNaked
+	ru, ri, rf, rl, rs, rb, rt reflect.Value // mapping to the primitives above
+}
+
+func (n *unsafeDecNakedWrapper) init() {
+	n.ru = reflect.ValueOf(&n.u).Elem()
+	n.ri = reflect.ValueOf(&n.i).Elem()
+	n.rf = reflect.ValueOf(&n.f).Elem()
+	n.rl = reflect.ValueOf(&n.l).Elem()
+	n.rs = reflect.ValueOf(&n.s).Elem()
+	n.rt = reflect.ValueOf(&n.t).Elem()
+	n.rb = reflect.ValueOf(&n.b).Elem()
+	// n.rr[] = reflect.ValueOf(&n.)
+}
+
+var defUnsafeDecNakedWrapper unsafeDecNakedWrapper
+
+func init() {
+	defUnsafeDecNakedWrapper.init()
+}
+
+func (n *decNaked) ru() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.ru
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.u)
+	return
+}
+func (n *decNaked) ri() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.ri
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.i)
+	return
+}
+func (n *decNaked) rf() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.rf
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.f)
+	return
+}
+func (n *decNaked) rl() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.rl
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.l)
+	return
+}
+func (n *decNaked) rs() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.rs
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.s)
+	return
+}
+func (n *decNaked) rt() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.rt
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.t)
+	return
+}
+func (n *decNaked) rb() (v reflect.Value) {
+	v = defUnsafeDecNakedWrapper.rb
+	((*unsafeReflectValue)(unsafe.Pointer(&v))).ptr = unsafe.Pointer(&n.b)
+	return
+}
+
 // --------------------------
 func (d *Decoder) raw(f *codecFnInfo, rv reflect.Value) {
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))

+ 1 - 1
codec/json.go

@@ -1211,7 +1211,7 @@ func (d *jsonDecDriver) bsToString() string {
 }
 
 func (d *jsonDecDriver) DecodeNaked() {
-	z := d.d.n
+	z := d.d.naked()
 	// var decodeFurther bool
 
 	if d.tok == 0 {

+ 1 - 1
codec/msgpack.go

@@ -460,7 +460,7 @@ func (d *msgpackDecDriver) DecodeNaked() {
 		d.readNextBd()
 	}
 	bd := d.bd
-	n := d.d.n
+	n := d.d.naked()
 	var decodeFurther bool
 
 	switch bd {

+ 1 - 1
codec/simple.go

@@ -538,7 +538,7 @@ func (d *simpleDecDriver) DecodeNaked() {
 		d.readNextBd()
 	}
 
-	n := d.d.n
+	n := d.d.naked()
 	var decodeFurther bool
 
 	switch d.bd {