Browse Source

codec: add more optimized unsafe versions of reflect and expand slices in batches

- add optimized unsafe versions for slice len/cap, slice, bytes, and Set
- decode slice: do not expand slice 1 element at a time
Ugorji Nwoke 6 years ago
parent
commit
6fd7dc6ca2
6 changed files with 185 additions and 72 deletions
  1. 1 1
      codec/binc.go
  2. 94 59
      codec/decode.go
  3. 11 7
      codec/encode.go
  4. 1 1
      codec/helper.go
  5. 26 0
      codec/helper_not_unsafe.go
  6. 52 4
      codec/helper_unsafe.go

+ 1 - 1
codec/binc.go

@@ -1087,7 +1087,7 @@ func (d *bincDecDriver) atEndOfDecode() {
 //       Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
 //
 func bincEncodeTime(t time.Time) []byte {
-	//t := rv.Interface().(time.Time)
+	//t := rv2i(rv).(time.Time)
 	tsecs, tnsecs := t.Unix(), t.Nanosecond()
 	var (
 		bd   byte

+ 94 - 59
codec/decode.go

@@ -463,7 +463,7 @@ func (d *Decoder) kInterfaceNaked(f *codecFnInfo) (rvn reflect.Value) {
 			d.decode(&v2)
 			rvn = rv4i(&v2).Elem()
 			if reflectArrayOfSupported && d.stid == 0 && d.h.PreferArrayOverSlice {
-				rvn2 := rvzeroaddrk(reflectArrayOf(rvn.Len(), intfTyp), reflect.Array)
+				rvn2 := rvzeroaddrk(reflectArrayOf(rvSliceLen(rvn), intfTyp), reflect.Array)
 				reflect.Copy(rvn2, rvn)
 				rvn = rvn2
 			}
@@ -587,7 +587,8 @@ func (d *Decoder) kInterface(f *codecFnInfo, rv reflect.Value) {
 	}
 
 	rvn2 := rvzeroaddrk(rvn.Type(), rvn.Kind())
-	rvn2.Set(rvn)
+	rvSetDirect(rvn2, rvn)
+	// rvn2.Set(rvn)
 	d.decodeValue(rvn2, nil)
 	rv.Set(rvn2)
 }
@@ -615,7 +616,7 @@ func (d *Decoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 	ctyp := d.d.ContainerType()
 	// xdebugf("kStruct: rv: %#v", rv)
 	if ctyp == valueTypeNil {
-		rv.Set(f.ti.rv0)
+		rvSetDirect(rv, f.ti.rv0)
 		return
 	}
 	var mf MissingFielder
@@ -713,13 +714,15 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	// A slice can be set from a map or array in stream.
 	// This way, the order can be kept (as order is lost with map).
 
+	// Note: rv is a slice type here - guaranteed
+
 	rtelem0 := f.ti.elem
 	ctyp := d.d.ContainerType()
 	if ctyp == valueTypeNil {
 		// xdebug2f("rv: %v, type: %v, canset: %v", rv, rv.Type(), rv.CanSet())
 		// rv.Set(reflect.New(f.ti.rt).Elem())
 		if rv.CanSet() {
-			rv.Set(f.ti.rv0)
+			rvSetDirect(rv, f.ti.rv0)
 		}
 		return
 	}
@@ -728,12 +731,12 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 		if !(f.ti.rtid == uint8SliceTypId || rtelem0.Kind() == reflect.Uint8) {
 			d.errorf("bytes/string in stream must decode into slice/array of bytes, not %v", f.ti.rt)
 		}
-		rvbs := rv.Bytes()
+		rvbs := rvGetBytes(rv)
 		bs2 := d.d.DecodeBytes(rvbs, false)
 		// if rvbs == nil && bs2 != nil || rvbs != nil && bs2 == nil || len(bs2) != len(rvbs) {
 		if !(len(bs2) > 0 && len(bs2) == len(rvbs) && &bs2[0] == &rvbs[0]) {
 			if rv.CanSet() {
-				rv.SetBytes(bs2)
+				rvSetBytes(rv, bs2)
 			} else if len(rvbs) > 0 && len(bs2) > 0 {
 				copy(rvbs, bs2)
 			}
@@ -758,7 +761,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	if containerLenS == 0 {
 		if rv.CanSet() {
 			if rvisnil(rv) {
-				rv.Set(reflect.MakeSlice(f.ti.rt, 0, 0))
+				rvSetDirect(rv, reflect.MakeSlice(f.ti.rt, 0, 0))
 			} else {
 				rvssetlen(rv, 0)
 			}
@@ -784,8 +787,8 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	var rv0 = rv
 	var rv9 reflect.Value
 
-	rvlen := rv.Len()
-	rvcap := rv.Cap()
+	rvlen := rvSliceLen(rv)
+	rvcap := rvSliceCap(rv)
 	hasLen := containerLenS > 0
 	if hasLen {
 		if containerLenS > rvcap {
@@ -823,6 +826,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	var rtelem0ZeroValid bool
 	var j int
 
+	// xdebug2f("0: rvcap: %d, rvlen: %d", rvcap, rvlen)
 	for ; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
 		if j == 0 && f.seq == seqTypeSlice && rvisnil(rv) {
 			if hasLen {
@@ -832,6 +836,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 			}
 			if rvCanset {
 				rv = reflect.MakeSlice(f.ti.rt, rvlen, rvlen)
+				rvcap = rvlen
 				rvChanged = true
 			} else {
 				d.errorf("cannot decode into non-settable slice")
@@ -853,19 +858,54 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 				return
 			}
 			// rv = reflect.Append(rv, reflect.Zero(rtelem0)) // append logic + varargs
-			var rvcap2 int
-			var rvErrmsg2 string
-			rv9, rvcap2, rvChanged, rvErrmsg2 =
-				expandSliceRV(rv, f.ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
-			if rvErrmsg2 != "" {
-				d.errorf(rvErrmsg2)
-			}
-			rvlen++
-			if rvChanged {
+
+			// expand the slice up to the cap.
+			// Note that we did, so we have to reset it later.
+
+			// xdebug2f("rvcap: %d, rvlen: %d", rvcap, rvlen)
+			if rvlen < rvcap {
+				if rv.CanSet() {
+					rvssetlen(rv, rvcap)
+					// xdebugf("after rvssetlen rv.Len: %d", rv.Len())
+				} else if rvCanset {
+					rv = rvSlice(rv, rvcap)
+					// xdebugf("after rvCanset rv.Len: %d", rv.Len())
+					rvChanged = true
+				} else {
+					d.errorf(errmsgExpandSliceCannotChange)
+					return
+				}
+				rvlen = rvcap
+			} else {
+				if !rvCanset {
+					d.errorf(errmsgExpandSliceCannotChange)
+					return
+				}
+				// xdebugf("else: before growCap: rvcap: %d", rvcap)
+				rvcap = growCap(rvcap, rtelem0Size, rvcap)
+				// rvcap = growCap(rvcap, rtelem0Size, rvcap+1+(rvcap*1/3))
+				rv9 = reflect.MakeSlice(f.ti.rt, rvcap, rvcap)
+				// xdebugf("else: rv9.Len: %d, rvcap: %d", rv9.Len(), rvcap)
+				reflect.Copy(rv9, rv)
 				rv = rv9
-				rvcap = rvcap2
+				rvChanged = true
+				rvlen = rvcap
 			}
+
+			// var rvcap2 int
+			// var rvErrmsg2 string
+			// rv9, rvcap2, rvChanged, rvErrmsg2 =
+			// 	expandSliceRV(rv, f.ti.rt, rvCanset, rtelem0Size, 1, rvlen, rvcap)
+			// if rvErrmsg2 != "" {
+			// 	d.errorf(rvErrmsg2)
+			// }
+			// rvlen++
+			// if rvChanged {
+			// 	rv = rv9
+			// 	rvcap = rvcap2
+			// }
 		}
+		// xdebugf("rv.Len: %d, j: %d", rv.Len(), j)
 		rv9 = rv.Index(j)
 		if d.h.SliceElementReset {
 			if !rtelem0ZeroValid {
@@ -884,7 +924,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 		if rv.CanSet() {
 			rvssetlen(rv, j)
 		} else if rvCanset {
-			rv = rv.Slice(0, j)
+			rv = rvSlice(rv, j)
 			rvChanged = true
 		} // else { d.errorf("kSlice: cannot change non-settable slice") }
 		rvlen = j
@@ -912,7 +952,7 @@ func (d *Decoder) kSliceForChan(f *codecFnInfo, rv reflect.Value) {
 	rtelem0 := f.ti.elem
 	ctyp := d.d.ContainerType()
 	if ctyp == valueTypeNil {
-		rv.Set(f.ti.rv0)
+		rvSetDirect(rv, f.ti.rv0)
 		return
 	}
 	if ctyp == valueTypeBytes || ctyp == valueTypeString {
@@ -939,7 +979,7 @@ func (d *Decoder) kSliceForChan(f *codecFnInfo, rv reflect.Value) {
 	// // handle if nil
 	// if slh.IsNil {
 	// 	if rv.CanSet() && !rvisnil(rv) {
-	// 		rv.Set(reflect.Zero(f.ti.rt))
+	// 		rvSetDirect(rv, reflect.Zero(f.ti.rt))
 	// 	}
 	// 	return
 	// }
@@ -947,7 +987,7 @@ func (d *Decoder) kSliceForChan(f *codecFnInfo, rv reflect.Value) {
 	// an array can never return a nil slice. so no need to check f.array here.
 	if containerLenS == 0 {
 		if rv.CanSet() && rvisnil(rv) {
-			rv.Set(reflect.MakeChan(f.ti.rt, 0))
+			rvSetDirect(rv, reflect.MakeChan(f.ti.rt, 0))
 		}
 		slh.End()
 		return
@@ -1023,13 +1063,13 @@ func (d *Decoder) kSliceForChan(f *codecFnInfo, rv reflect.Value) {
 func (d *Decoder) kMap(f *codecFnInfo, rv reflect.Value) {
 	containerLen := d.mapStart()
 	if containerLen == decContainerLenNil {
-		rv.Set(f.ti.rv0)
+		rvSetDirect(rv, f.ti.rv0)
 		return
 	}
 	ti := f.ti
 	if rvisnil(rv) {
 		rvlen := decInferLen(containerLen, d.h.MaxInitLen, int(ti.key.Size()+ti.elem.Size()))
-		rv.Set(makeMapReflect(ti.rt, rvlen))
+		rvSetDirect(rv, makeMapReflect(ti.rt, rvlen))
 	}
 
 	if containerLen == 0 {
@@ -1110,7 +1150,7 @@ func (d *Decoder) kMap(f *codecFnInfo, rv reflect.Value) {
 		// special case if interface wrapping a byte array.
 		if ktypeIsIntf {
 			if rvk2 := rvk.Elem(); rvk2.IsValid() && rvk2.Type() == uint8SliceTyp {
-				rvk.Set(rv4i(d.string(rvk2.Bytes())))
+				rvk.Set(rv4i(d.string(rvGetBytes(rvk2))))
 			}
 			// NOTE: consider failing early if map/slice/func
 		}
@@ -1828,7 +1868,7 @@ func (d *Decoder) decodeValue(rv reflect.Value, fn *codecFn) {
 		rvpValid = true
 		for rv.Kind() == reflect.Ptr {
 			if rvisnil(rv) {
-				rv.Set(reflect.New(rv.Type().Elem()))
+				rvSetDirect(rv, reflect.New(rv.Type().Elem()))
 			}
 			rvp = rv
 			rv = rv.Elem()
@@ -2071,11 +2111,10 @@ func (d *Decoder) interfaceExtConvertAndDecode(v interface{}, ext Ext) {
 	if !rv.CanAddr() {
 		if rv.Kind() == reflect.Ptr {
 			rv2 = reflect.New(rv.Type().Elem())
-			rv2.Set(rv)
 		} else {
 			rv2 = rvzeroaddrk(rv.Type(), rv.Kind())
-			rv2.Set(rv)
 		}
+		rvSetDirect(rv2, rv)
 		rv = rv2
 	}
 	d.decodeValue(rv, nil)
@@ -2235,36 +2274,32 @@ func decInferLen(clen, maxlen, unit int) (rvlen int) {
 	return
 }
 
-func expandSliceRV(s reflect.Value, st reflect.Type, canChange bool, stElemSize, num, slen, scap int) (
-	s2 reflect.Value, scap2 int, changed bool, err string) {
-	l1 := slen + num // new slice length
-	if l1 < slen {
-		err = errmsgExpandSliceOverflow
-		return
-	}
-	if l1 <= scap {
-		if s.CanSet() {
-			rvssetlen(s, l1)
-		} else if canChange {
-			s2 = s.Slice(0, l1)
-			scap2 = scap
-			changed = true
-		} else {
-			err = errmsgExpandSliceCannotChange
-			return
-		}
-		return
-	}
-	if !canChange {
-		err = errmsgExpandSliceCannotChange
-		return
-	}
-	scap2 = growCap(scap, stElemSize, num)
-	s2 = reflect.MakeSlice(st, l1, scap2)
-	changed = true
-	reflect.Copy(s2, s)
-	return
-}
+// func expandSliceRV(s reflect.Value, st reflect.Type, canChange bool, stElemSize, num, slen, scap int) (
+// 	s2 reflect.Value, scap2 int, changed bool, err string) {
+// 	l1 := slen + num // new slice length
+// 	if l1 <= scap {
+// 		if s.CanSet() {
+// 			rvssetlen(s, l1)
+// 		} else if canChange {
+// 			s2 = rvSlice(s, l1)
+// 			scap2 = scap
+// 			changed = true
+// 		} else {
+// 			err = errmsgExpandSliceCannotChange
+// 			return
+// 		}
+// 		return
+// 	}
+// 	if !canChange {
+// 		err = errmsgExpandSliceCannotChange
+// 		return
+// 	}
+// 	scap2 = growCap(scap, stElemSize, num)
+// 	s2 = reflect.MakeSlice(st, l1, scap2)
+// 	changed = true
+// 	reflect.Copy(s2, s)
+// 	return
+// }
 
 func decReadFull(r io.Reader, bs []byte) (n uint, err error) {
 	var nn int

+ 11 - 7
codec/encode.go

@@ -345,7 +345,7 @@ func (e *Encoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 		// If in this method, then there was no extension function defined.
 		// So it's okay to treat as []byte.
 		if !mbs && f.ti.rtid == uint8SliceTypId {
-			e.e.EncodeStringBytesRaw(rv.Bytes())
+			e.e.EncodeStringBytesRaw(rvGetBytes(rv))
 			return
 		}
 	}
@@ -359,11 +359,11 @@ func (e *Encoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 	if !mbs && uint8TypId == rt2id(rtelem) { // NOT rtelem.Kind() == reflect.Uint8
 		switch f.seq {
 		case seqTypeSlice:
-			e.e.EncodeStringBytesRaw(rv.Bytes())
+			e.e.EncodeStringBytesRaw(rvGetBytes(rv))
 		case seqTypeArray:
 			l = rv.Len()
 			if rv.CanAddr() {
-				e.e.EncodeStringBytesRaw(rv.Slice(0, l).Bytes())
+				e.e.EncodeStringBytesRaw(rvGetBytes(rv.Slice(0, l)))
 			} else {
 				var bs []byte
 				if l <= cap(e.b) {
@@ -516,7 +516,7 @@ func (e *Encoder) kStruct(f *codecFnInfo, rv reflect.Value) {
 		} else {
 			// make a new addressable value of same one, and use it
 			rv2 := reflect.New(rv.Type())
-			rv2.Elem().Set(rv)
+			rvSetDirect(rv2.Elem(), rv)
 			mf = rv2i(rv2).(MissingFielder).CodecMissingFields()
 		}
 		toMap = true
@@ -1335,8 +1335,9 @@ TOP:
 		e.errorf("circular reference found: # %p, %T", sptr, sptr)
 	}
 
+	var rt reflect.Type
 	if fn == nil {
-		rt := rv.Type()
+		rt = rv.Type()
 		fn = e.h.fn(rt)
 	}
 	if fn.i.addrE {
@@ -1345,8 +1346,11 @@ TOP:
 		} else if rv.CanAddr() {
 			fn.fe(e, &fn.i, rv.Addr())
 		} else {
-			rv2 := reflect.New(rv.Type())
-			rv2.Elem().Set(rv)
+			if rt == nil {
+				rt = rv.Type()
+			}
+			rv2 := reflect.New(rt)
+			rvSetDirect(rv2.Elem(), rv)
 			fn.fe(e, &fn.i, rv2)
 		}
 	} else {

+ 1 - 1
codec/helper.go

@@ -1544,7 +1544,7 @@ func baseStructRv(v reflect.Value, update bool) (v2 reflect.Value, valid bool) {
 			if !update {
 				return
 			}
-			v.Set(reflect.New(v.Type().Elem()))
+			rvSetDirect(v, reflect.New(v.Type().Elem()))
 		}
 		v = v.Elem()
 	}

+ 26 - 0
codec/helper_not_unsafe.go

@@ -272,10 +272,36 @@ func rvSetUint64(rv reflect.Value, v uint64) {
 
 // ----------------
 
+// rvSetDirect is rv.Set for all kinds except reflect.Interface
+func rvSetDirect(rv reflect.Value, v reflect.Value) {
+	rv.Set(v)
+}
+
+// ----------------
+
+func rvSliceLen(rv reflect.Value) int {
+	return rv.Len()
+}
+
+func rvSliceCap(rv reflect.Value) int {
+	return rv.Cap()
+}
+
+// rvSlice returns a slice of the slice of lenth
+func rvSlice(rv reflect.Value, length int) reflect.Value {
+	return rv.Slice(0, length)
+}
+
+// ----------------
+
 func rvGetBool(rv reflect.Value) bool {
 	return rv.Bool()
 }
 
+func rvGetBytes(rv reflect.Value) []byte {
+	return rv.Bytes()
+}
+
 func rvGetTime(rv reflect.Value) time.Time {
 	return rv2i(rv).(time.Time)
 }

+ 52 - 4
codec/helper_unsafe.go

@@ -485,6 +485,49 @@ func rvSetUint64(rv reflect.Value, v uint64) {
 	*(*uint64)(urv.ptr) = v
 }
 
+// ----------------
+
+// rvSetDirect is rv.Set for all kinds except reflect.Interface
+func rvSetDirect(rv reflect.Value, v reflect.Value) {
+	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
+	if uv.flag&unsafeFlagIndir == 0 {
+		*(*unsafe.Pointer)(urv.ptr) = uv.ptr
+	} else {
+		typedmemmove(urv.typ, urv.ptr, uv.ptr)
+	}
+
+}
+
+// ------------
+
+func rvSliceLen(rv reflect.Value) int {
+	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	return (*unsafeSlice)(urv.ptr).Len
+}
+
+func rvSliceCap(rv reflect.Value) int {
+	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	return (*unsafeSlice)(urv.ptr).Cap
+}
+
+// rvSlice returns a slice of the slice of lenth
+func rvSlice(rv reflect.Value, length int) (v reflect.Value) {
+	if false {
+		return rv.Slice(0, length)
+	}
+	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
+	*uv = *urv
+	var x []unsafe.Pointer
+	uv.ptr = unsafe.Pointer(&x)
+	*(*unsafeSlice)(uv.ptr) = *(*unsafeSlice)(urv.ptr)
+	(*unsafeSlice)(uv.ptr).Len = length
+	// xdebugf("length: %d, slice: from: %#v, to: %#v",
+	// 	length, *(*unsafeSlice)(urv.ptr), *(*unsafeSlice)(uv.ptr))
+	return
+}
+
 // ------------
 
 func rvGetBool(rv reflect.Value) bool {
@@ -492,6 +535,11 @@ func rvGetBool(rv reflect.Value) bool {
 	return *(*bool)(v.ptr)
 }
 
+func rvGetBytes(rv reflect.Value) []byte {
+	v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	return *(*[]byte)(v.ptr)
+}
+
 func rvGetTime(rv reflect.Value) time.Time {
 	v := (*unsafeReflectValue)(unsafe.Pointer(&rv))
 	return *(*time.Time)(v.ptr)
@@ -630,9 +678,9 @@ func (t *unsafeMapIter) Next() (r bool) {
 	if t.done {
 		return
 	}
-	unsafeSet(t.kptr, t.ktyp, t.it.key, t.kisref)
+	unsafeMapSet(t.kptr, t.ktyp, t.it.key, t.kisref)
 	if t.mapvalues {
-		unsafeSet(t.vptr, t.vtyp, t.it.value, t.visref)
+		unsafeMapSet(t.vptr, t.vtyp, t.it.value, t.visref)
 	}
 	return true
 }
@@ -655,7 +703,7 @@ func (t *unsafeMapIter) Done() {
 	}
 }
 
-func unsafeSet(p, ptyp, p2 unsafe.Pointer, isref bool) {
+func unsafeMapSet(p, ptyp, p2 unsafe.Pointer, isref bool) {
 	if isref {
 		*(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(p2) // p2
 	} else {
@@ -719,7 +767,7 @@ func mapGet(m, k, v reflect.Value) (vv reflect.Value) {
 
 	urv = (*unsafeReflectValue)(unsafe.Pointer(&v))
 
-	unsafeSet(urv.ptr, urv.typ, vvptr, refBitset.isset(byte(v.Kind())))
+	unsafeMapSet(urv.ptr, urv.typ, vvptr, refBitset.isset(byte(v.Kind())))
 	return v
 }