瀏覽代碼

codec: optimize iteration of slices/array/chan

Ugorji Nwoke 6 年之前
父節點
當前提交
d192c3eb23
共有 5 個文件被更改,包括 141 次插入68 次删除
  1. 1 1
      codec/decode.go
  2. 113 62
      codec/encode.go
  3. 14 5
      codec/helper.go
  4. 4 0
      codec/helper_not_unsafe.go
  5. 9 0
      codec/helper_unsafe.go

+ 1 - 1
codec/decode.go

@@ -815,7 +815,7 @@ func (d *Decoder) kSlice(f *codecFnInfo, rv reflect.Value) {
 				rvlen = rvcap
 			}
 		}
-		rv9 = rv.Index(j)
+		rv9 = rvSliceIndex(rv, j, f.ti)
 		if d.h.SliceElementReset {
 			if !rtelem0ZeroValid {
 				rtelem0ZeroValid = true

+ 113 - 62
codec/encode.go

@@ -256,8 +256,8 @@ func (e *Encoder) kErr(f *codecFnInfo, rv reflect.Value) {
 	e.errorf("unsupported kind %s, for %#v", rv.Kind(), rv)
 }
 
-func chanToSlice(rv reflect.Value, rtelem reflect.Type, timeout time.Duration) (rvcs reflect.Value) {
-	rvcs = reflect.Zero(reflect.SliceOf(rtelem))
+func chanToSlice(rv reflect.Value, rtslice reflect.Type, timeout time.Duration) (rvcs reflect.Value) {
+	rvcs = reflect.Zero(rtslice)
 	if timeout < 0 { // consume until close
 		for {
 			recv, recvOk := rv.Recv()
@@ -286,88 +286,139 @@ func chanToSlice(rv reflect.Value, rtelem reflect.Type, timeout time.Duration) (
 	return
 }
 
-func (e *Encoder) kSlice(f *codecFnInfo, rv reflect.Value) {
-	// array may be non-addressable, so we have to manage with care
-	//   (don't call rv.Bytes, rv.Slice, etc).
-	// E.g. type struct S{B [2]byte};
-	//   Encode(S{}) will bomb on "panic: slice of unaddressable array".
-	mbs := f.ti.mbs
-	if f.seq != seqTypeArray {
-		if rvIsNil(rv) {
-			e.e.EncodeNil()
-			return
-		}
-		// 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(rvGetBytes(rv))
-			return
-		}
+func (e *Encoder) kSeqFn(rtelem reflect.Type) (fn *codecFn) {
+	for rtelem.Kind() == reflect.Ptr {
+		rtelem = rtelem.Elem()
 	}
-	if f.seq == seqTypeChan && f.ti.chandir&uint8(reflect.RecvDir) == 0 {
-		e.errorf("send-only channel cannot be encoded")
+	// if kind is reflect.Interface, do not pre-determine the
+	// encoding type, because preEncodeValue may break it down to
+	// a concrete type and kInterface will bomb.
+	if rtelem.Kind() != reflect.Interface {
+		fn = e.h.fn(rtelem)
 	}
-	rtelem := f.ti.elem
+	return
+}
 
-	var l int
-	// if a slice, array or chan of bytes, treat specially
-	if !mbs && uint8TypId == rt2id(rtelem) { // NOT rtelem.Kind() == reflect.Uint8
-		switch f.seq {
-		case seqTypeSlice:
-			e.e.EncodeStringBytesRaw(rvGetBytes(rv))
-		case seqTypeArray:
-			e.e.EncodeStringBytesRaw(rvGetArrayBytesRO(rv, e.b[:]))
-		case seqTypeChan:
-			e.kSliceBytesChan(rv)
+func (e *Encoder) kSliceWMbs(rv reflect.Value, ti *typeInfo) {
+	var l = rvGetSliceLen(rv)
+	if l == 0 {
+		e.mapStart(0)
+	} else {
+		if l%2 == 1 {
+			e.errorf("mapBySlice requires even slice length, but got %v", l)
+			return
+		}
+		e.mapStart(l / 2)
+		fn := e.kSeqFn(ti.elem)
+		for j := 0; j < l; j++ {
+			if j%2 == 0 {
+				e.mapElemKey()
+			} else {
+				e.mapElemValue()
+			}
+			e.encodeValue(rvSliceIndex(rv, j, ti), fn)
 		}
-		return
 	}
+	e.mapEnd()
+}
 
-	// if chan, consume chan into a slice, and work off that slice.
-	if f.seq == seqTypeChan {
-		rv = chanToSlice(rv, rtelem, e.h.ChanRecvTimeout)
+func (e *Encoder) kSliceW(rv reflect.Value, ti *typeInfo) {
+	var l = rvGetSliceLen(rv)
+	e.arrayStart(l)
+	if l > 0 {
+		fn := e.kSeqFn(ti.elem)
+		for j := 0; j < l; j++ {
+			e.arrayElem()
+			e.encodeValue(rvSliceIndex(rv, j, ti), fn)
+		}
 	}
+	e.arrayEnd()
+}
 
-	l = rv.Len() // rv may be slice or array
-	if mbs {
+func (e *Encoder) kSeqWMbs(rv reflect.Value, ti *typeInfo) {
+	var l = rv.Len()
+	if l == 0 {
+		e.mapStart(0)
+	} else {
 		if l%2 == 1 {
 			e.errorf("mapBySlice requires even slice length, but got %v", l)
 			return
 		}
 		e.mapStart(l / 2)
-	} else {
-		e.arrayStart(l)
+		fn := e.kSeqFn(ti.elem)
+		for j := 0; j < l; j++ {
+			if j%2 == 0 {
+				e.mapElemKey()
+			} else {
+				e.mapElemValue()
+			}
+			e.encodeValue(rv.Index(j), fn)
+		}
 	}
+	e.mapEnd()
+}
 
+func (e *Encoder) kSeqW(rv reflect.Value, ti *typeInfo) {
+	var l = rv.Len()
+	e.arrayStart(l)
 	if l > 0 {
-		var fn *codecFn
-		for rtelem.Kind() == reflect.Ptr {
-			rtelem = rtelem.Elem()
-		}
-		// if kind is reflect.Interface, do not pre-determine the
-		// encoding type, because preEncodeValue may break it down to
-		// a concrete type and kInterface will bomb.
-		if rtelem.Kind() != reflect.Interface {
-			fn = e.h.fn(rtelem)
-		}
+		fn := e.kSeqFn(ti.elem)
 		for j := 0; j < l; j++ {
-			if mbs {
-				if j%2 == 0 {
-					e.mapElemKey()
-				} else {
-					e.mapElemValue()
-				}
-			} else {
-				e.arrayElem()
-			}
+			e.arrayElem()
 			e.encodeValue(rv.Index(j), fn)
 		}
 	}
+	e.arrayEnd()
+}
 
-	if mbs {
-		e.mapEnd()
+func (e *Encoder) kChan(f *codecFnInfo, rv reflect.Value) {
+	if rvIsNil(rv) {
+		e.e.EncodeNil()
+		return
+	}
+	if f.ti.chandir&uint8(reflect.RecvDir) == 0 {
+		e.errorf("send-only channel cannot be encoded")
+		return
+	}
+	if !f.ti.mbs && uint8TypId == rt2id(f.ti.elem) {
+		e.kSliceBytesChan(rv)
+		return
+	}
+	rtslice := reflect.SliceOf(f.ti.elem)
+	rv = chanToSlice(rv, rtslice, e.h.ChanRecvTimeout)
+	ti := e.h.getTypeInfo(rt2id(rtslice), rtslice)
+	if f.ti.mbs {
+		e.kSliceWMbs(rv, ti)
 	} else {
-		e.arrayEnd()
+		e.kSliceW(rv, ti)
+	}
+}
+
+func (e *Encoder) kSlice(f *codecFnInfo, rv reflect.Value) {
+	if rvIsNil(rv) {
+		e.e.EncodeNil()
+		return
+	}
+	if f.ti.mbs {
+		e.kSliceWMbs(rv, f.ti)
+	} else {
+		if f.ti.rtid == uint8SliceTypId || uint8TypId == rt2id(f.ti.elem) {
+			e.e.EncodeStringBytesRaw(rvGetBytes(rv))
+		} else {
+			e.kSliceW(rv, f.ti)
+		}
+	}
+}
+
+func (e *Encoder) kArray(f *codecFnInfo, rv reflect.Value) {
+	if f.ti.mbs {
+		e.kSeqWMbs(rv, f.ti)
+	} else {
+		if uint8TypId == rt2id(f.ti.elem) {
+			e.e.EncodeStringBytesRaw(rvGetArrayBytesRO(rv, e.b[:]))
+		} else {
+			e.kSeqW(rv, f.ti)
+		}
 	}
 }
 

+ 14 - 5
codec/helper.go

@@ -932,7 +932,7 @@ func (x *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *
 				fn.fd = (*Decoder).kErr
 			case reflect.Chan:
 				fi.seq = seqTypeChan
-				fn.fe = (*Encoder).kSlice
+				fn.fe = (*Encoder).kChan
 				fn.fd = (*Decoder).kSliceForChan
 			case reflect.Slice:
 				fi.seq = seqTypeSlice
@@ -940,7 +940,7 @@ func (x *BasicHandle) fnLoad(rt reflect.Type, rtid uintptr, checkExt bool) (fn *
 				fn.fd = (*Decoder).kSlice
 			case reflect.Array:
 				fi.seq = seqTypeArray
-				fn.fe = (*Encoder).kSlice
+				fn.fe = (*Encoder).kArray
 				fi.addrF = false
 				fi.addrD = false
 				rt2 := reflect.SliceOf(ti.elem)
@@ -1584,13 +1584,16 @@ type typeInfo struct {
 	// so beneficial for intXX, bool, slices, structs, etc
 	rv0 reflect.Value
 
+	elemsize uintptr
+
 	// other flags, with individual bits representing if set.
 	flags tiflag
 
 	infoFieldOmitempty bool
 
-	_ [3]byte   // padding
-	_ [1]uint64 // padding
+	elemkind uint8
+	_        [2]byte // padding
+	// _ [1]uint64 // padding
 }
 
 func (ti *typeInfo) isFlag(f tiflag) bool {
@@ -1756,10 +1759,16 @@ func (x *TypeInfos) get(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
 	case reflect.Slice:
 		ti.mbs, _ = implIntf(rt, mapBySliceTyp)
 		ti.elem = rt.Elem()
+		ti.elemsize = ti.elem.Size()
+		ti.elemkind = uint8(ti.elem.Kind())
 	case reflect.Chan:
 		ti.elem = rt.Elem()
 		ti.chandir = uint8(rt.ChanDir())
-	case reflect.Array, reflect.Ptr:
+	case reflect.Array:
+		ti.elem = rt.Elem()
+		ti.elemsize = ti.elem.Size()
+		ti.elemkind = uint8(ti.elem.Kind())
+	case reflect.Ptr:
 		ti.elem = rt.Elem()
 	}
 

+ 4 - 0
codec/helper_not_unsafe.go

@@ -260,6 +260,10 @@ func rvSlice(rv reflect.Value, length int) reflect.Value {
 
 // ----------------
 
+func rvSliceIndex(rv reflect.Value, i int, ti *typeInfo) reflect.Value {
+	return rv.Index(i)
+}
+
 func rvGetSliceLen(rv reflect.Value) int {
 	return rv.Len()
 }

+ 9 - 0
codec/helper_unsafe.go

@@ -475,6 +475,15 @@ func rvSlice(rv reflect.Value, length int) (v reflect.Value) {
 
 // ------------
 
+func rvSliceIndex(rv reflect.Value, i int, ti *typeInfo) (v reflect.Value) {
+	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
+	uv := (*unsafeReflectValue)(unsafe.Pointer(&v))
+	uv.ptr = unsafe.Pointer(uintptr(((*unsafeSlice)(urv.ptr)).Data) + (ti.elemsize * uintptr(i)))
+	uv.typ = ((*unsafeIntf)(unsafe.Pointer(&ti.elem))).word
+	uv.flag = uintptr(ti.elemkind) | unsafeFlagIndir | unsafeFlagAddr
+	return
+}
+
 func rvGetSliceLen(rv reflect.Value) int {
 	urv := (*unsafeReflectValue)(unsafe.Pointer(&rv))
 	return (*unsafeSlice)(urv.ptr).Len