Browse Source

codec: fast-path: optimize: remove unnecessary local variables

rearrange structs so that commonly accessed fields are at top of any cache lines,
and within 1st 2 cache lines of the enclosing struct.

Do not copy encDriver, decDriver, esep into local variables.
Instead, directly use the field in *Decoder or *Encoder, as these are performant
to access (since registers are not used for local variables).
Ugorji Nwoke 6 years ago
parent
commit
eb5e2717b6
6 changed files with 319 additions and 358 deletions
  1. 6 7
      codec/decode.go
  2. 7 7
      codec/encode.go
  3. 242 277
      codec/fast-path.generated.go
  4. 38 41
      codec/fast-path.go.tmpl
  5. 25 25
      codec/gen.go
  6. 1 1
      codec/simple.go

+ 6 - 7
codec/decode.go

@@ -1996,21 +1996,20 @@ type decNaked struct {
 // --------------
 
 type decReaderSwitch struct {
-	rb bytesDecReader
-	// ---- cpu cache line boundary?
-	ri *ioDecReader
-	bi *bufioDecReader
-
+	esep     bool // has elem separators
 	mtr, str bool // whether maptype or slicetype are known types
 
 	be   bool // is binary encoding
 	js   bool // is json handle
 	jsms bool // is json handle, and MapKeyAsString
-	esep bool // has elem separators
 
 	// typ   entryType
 	bytes bool // is bytes reader
 	bufio bool // is this a bufioDecReader?
+
+	rb bytesDecReader
+	ri *ioDecReader
+	bi *bufioDecReader
 }
 
 // numread, track and stopTrack are always inlined, as they just check int fields, etc.
@@ -2290,8 +2289,8 @@ type Decoder struct {
 	mtid uintptr
 	stid uintptr
 
-	hh Handle
 	h  *BasicHandle
+	hh Handle
 
 	// ---- cpu cache line boundary?
 	decReaderSwitch

+ 7 - 7
codec/encode.go

@@ -1106,18 +1106,18 @@ func (e *Encoder) kMapCanonical(rtkey reflect.Type, rv reflect.Value, mks []refl
 // // --------------------------------------------------
 
 type encWriterSwitch struct {
-	// wi   *ioEncWriter
-	wb bytesEncAppender
-	wf *bufioEncWriter
-	// typ  entryType
-	bytes bool // encoding to []byte
 	esep  bool // whether it has elem separators
+	bytes bool // encoding to []byte
 	isas  bool // whether e.as != nil
 	js    bool // is json encoder?
 	be    bool // is binary encoder?
-	// _     [2]byte // padding
+	// _    [3]byte // padding
 	// _    [2]uint64 // padding
 	// _    uint64    // padding
+	// wi   *ioEncWriter
+	wb bytesEncAppender
+	wf *bufioEncWriter
+	// typ  entryType
 }
 
 func (z *encWriterSwitch) writeb(s []byte) {
@@ -1284,7 +1284,7 @@ type Encoder struct {
 	h  *BasicHandle
 	hh Handle
 
-	// ---- cpu cache line boundary? + 3
+	// ---- cpu cache line boundary
 	encWriterSwitch
 
 	err error

File diff suppressed because it is too large
+ 242 - 277
codec/fast-path.generated.go


+ 38 - 41
codec/fast-path.go.tmpl

@@ -187,34 +187,32 @@ func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv r
 		fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).([]{{ .Elem }}), e)
 	}
 }
-func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) {
+func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, e *Encoder) {
 	if v == nil { e.e.EncodeNil(); return }
-	ee, esep := e.e, e.hh.hasElemSeparators()
-	ee.WriteArrayStart(len(v))
+	e.e.WriteArrayStart(len(v))
 	for _, v2 := range v {
-		if esep { ee.WriteArrayElem() }
+		if e.esep { e.e.WriteArrayElem() }
 		{{ encmd .Elem "v2"}}
 	} 
-	ee.WriteArrayEnd()
+	e.e.WriteArrayEnd()
 }
-func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) {
-	ee, esep := e.e, e.hh.hasElemSeparators()
+func (fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *Encoder) {
 	if len(v)%2 == 1 {
 		e.errorf(fastpathMapBySliceErrMsg, len(v))
 		return
 	}
-	ee.WriteMapStart(len(v) / 2)
+	e.e.WriteMapStart(len(v) / 2)
 	for j, v2 := range v {
-		if esep {
+		if e.esep {
 			if j%2 == 0 {
-				ee.WriteMapElemKey()
+				e.e.WriteMapElemKey()
 			} else {
-				ee.WriteMapElemValue()
+				e.e.WriteMapElemValue()
 			}
 		}
 		{{ encmd .Elem "v2"}}
 	}
-	ee.WriteMapEnd()
+	e.e.WriteMapEnd()
 }
 {{end}}{{end}}{{end}}
 
@@ -222,10 +220,9 @@ func (_ fastpathT) {{ .MethodNamePfx "EncAsMap" false }}V(v []{{ .Elem }}, e *En
 func (e *Encoder) {{ .MethodNamePfx "fastpathEnc" false }}R(f *codecFnInfo, rv reflect.Value) {
 	fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv2i(rv).(map[{{ .MapKey }}]{{ .Elem }}), e)
 }
-func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *Encoder) {
+func (fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, e *Encoder) {
 	if v == nil { e.e.EncodeNil(); return }
-	ee, esep := e.e, e.hh.hasElemSeparators() 
-	ee.WriteMapStart(len(v))
+	e.e.WriteMapStart(len(v))
 	if e.h.Canonical { {{/* need to figure out .NoCanonical */}}
 		{{if eq .MapKey "interface{}"}}{{/* out of band 
 		*/}}var mksv []byte = make([]byte, 0, len(v)*16) // temporary byte slice for the encoding
@@ -243,9 +240,9 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		}
 		sort.Sort(bytesIntfSlice(v2))
 		for j := range v2 {
-			if esep { ee.WriteMapElemKey() }
+			if e.esep { e.e.WriteMapElemKey() }
 			e.asis(v2[j].v)
-			if esep { ee.WriteMapElemValue() }
+			if e.esep { e.e.WriteMapElemValue() }
 			e.encode(v[v2[j].i])
 		} {{else}}{{ $x := sorttype .MapKey true}}v2 := make([]{{ $x }}, len(v))
 		var i uint
@@ -255,20 +252,20 @@ func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		}
 		sort.Sort({{ sorttype .MapKey false}}(v2))
 		for _, k2 := range v2 {
-			if esep { ee.WriteMapElemKey() }
-			{{if eq .MapKey "string"}} if e.h.StringToRaw {ee.EncodeStringBytesRaw(bytesView(k2))} else {ee.EncodeStringEnc(cUTF8, k2)} {{else}}{{ $y := printf "%s(k2)" .MapKey }}{{if eq $x .MapKey }}{{ $y = "k2" }}{{end}}{{ encmd .MapKey $y }}{{end}}
-			if esep { ee.WriteMapElemValue() }
+			if e.esep { e.e.WriteMapElemKey() }
+			{{if eq .MapKey "string"}} if e.h.StringToRaw {e.e.EncodeStringBytesRaw(bytesView(k2))} else {e.e.EncodeStringEnc(cUTF8, k2)} {{else}}{{ $y := printf "%s(k2)" .MapKey }}{{if eq $x .MapKey }}{{ $y = "k2" }}{{end}}{{ encmd .MapKey $y }}{{end}}
+			if e.esep { e.e.WriteMapElemValue() }
 			{{ $y := printf "v[%s(k2)]" .MapKey }}{{if eq $x .MapKey }}{{ $y = "v[k2]" }}{{end}}{{ encmd .Elem $y }}
 		} {{end}}
 	} else { 
 		for k2, v2 := range v {
-			if esep { ee.WriteMapElemKey() }
-			{{if eq .MapKey "string"}} if e.h.StringToRaw {ee.EncodeStringBytesRaw(bytesView(k2))} else {ee.EncodeStringEnc(cUTF8, k2)} {{else}}{{ encmd .MapKey "k2"}}{{end}}
-			if esep { ee.WriteMapElemValue() }
+			if e.esep { e.e.WriteMapElemKey() }
+			{{if eq .MapKey "string"}} if e.h.StringToRaw {e.e.EncodeStringBytesRaw(bytesView(k2))} else {e.e.EncodeStringEnc(cUTF8, k2)} {{else}}{{ encmd .MapKey "k2"}}{{end}}
+			if e.esep { e.e.WriteMapElemValue() }
 			{{ encmd .Elem "v2"}}
 		}
 	}
-	ee.WriteMapEnd()
+	e.e.WriteMapEnd()
 }
 {{end}}{{end}}{{end}}
 
@@ -353,10 +350,10 @@ func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, d *Decod
 	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
 	if changed { *vp = v }
 }
-func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
-	dd := d.d{{/*
-		// if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil()
-	*/}}
+func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange bool, d *Decoder) (_ []{{ .Elem }}, changed bool) {
+	{{/* dd := d.d
+		// if d.d.isContainerType(valueTypeNil) { d.d.TryDecodeAsNil() }
+	 */ -}}
 	slh, containerLenS := d.decSliceHelperStart()
 	if containerLenS == 0 {
 		if canChange {
@@ -384,7 +381,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 		}
 	}
 	var j int
-	for j = 0; (hasLen && j < containerLenS) || !(hasLen || dd.CheckBreak()); j++ {
+	for j = 0; (hasLen && j < containerLenS) || !(hasLen || d.d.CheckBreak()); j++ {
 		if j == 0 && len(v) == 0 && canChange {
 			if hasLen {
 				xlen = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }})
@@ -408,7 +405,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, canChange
 		slh.ElemContainerState(j)
 		if decodeIntoBlank {
 			d.swallow()
-		} else if dd.TryDecodeAsNil() {
+		} else if d.d.TryDecodeAsNil() {
 			v[uint(j)] = {{ zerocmd .Elem }}
 		} else {
 			{{ if eq .Elem "interface{}" }}d.decode(&v[uint(j)]){{ else }}v[uint(j)] = {{ decmd .Elem }}{{ end }}
@@ -448,19 +445,19 @@ func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .E
 	v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, true, d)
 	if changed { *vp = v }
 }
-func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
+func (fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, canChange bool, 
 	d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
-	dd, esep := d.d, d.hh.hasElemSeparators(){{/*
-		// if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil()
-	*/}}
-	containerLen := dd.ReadMapStart()
+    {{/*
+		// if d.d.isContainerType(valueTypeNil) {d.d.TryDecodeAsNil()
+	*/ -}}
+	containerLen := d.d.ReadMapStart()
 	if canChange && v == nil {
 		xlen := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
 		v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
 		changed = true
 	}
 	if containerLen == 0 {
-		dd.ReadMapEnd()
+		d.d.ReadMapEnd()
 		return v, changed
 	}
 	d.depthIncr()
@@ -468,15 +465,15 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 	{{end}}var mk {{ .MapKey }}
 	var mv {{ .Elem }}
 	hasLen := containerLen > 0
-	for j := 0; (hasLen && j < containerLen) || !(hasLen || dd.CheckBreak()); j++ {
-		if esep { dd.ReadMapElemKey() }
+	for j := 0; (hasLen && j < containerLen) || !(hasLen || d.d.CheckBreak()); j++ {
+		if d.esep { d.d.ReadMapElemKey() }
 		{{ if eq .MapKey "interface{}" }}mk = nil 
 		d.decode(&mk)
 		if bv, bok := mk.([]byte); bok {
 			mk = d.string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
 		}{{ else }}mk = {{ decmd .MapKey }}{{ end }}
-		if esep { dd.ReadMapElemValue() }
-		if dd.TryDecodeAsNil() {
+		if d.esep { d.d.ReadMapElemValue() }
+		if d.d.TryDecodeAsNil() {
 			if v == nil {} else if d.h.DeleteOnNilMapValue { delete(v, mk) } else { v[mk] = {{ zerocmd .Elem }} }
 			continue 
 		}
@@ -484,7 +481,7 @@ func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Ele
 		d.decode(&mv){{ else }}mv = {{ decmd .Elem }}{{ end }}
 		if v != nil { v[mk] = mv }
 	}
-	dd.ReadMapEnd()
+	d.d.ReadMapEnd()
 	d.depthDecr()
 	return v, changed
 }

+ 25 - 25
codec/gen.go

@@ -1962,24 +1962,24 @@ func genInternalNonZeroValue(s string) string {
 func genInternalEncCommandAsString(s string, vname string) string {
 	switch s {
 	case "uint64":
-		return "ee.EncodeUint(" + vname + ")"
+		return "e.e.EncodeUint(" + vname + ")"
 	case "uint", "uint8", "uint16", "uint32":
-		return "ee.EncodeUint(uint64(" + vname + "))"
+		return "e.e.EncodeUint(uint64(" + vname + "))"
 	case "int64":
-		return "ee.EncodeInt(" + vname + ")"
+		return "e.e.EncodeInt(" + vname + ")"
 	case "int", "int8", "int16", "int32":
-		return "ee.EncodeInt(int64(" + vname + "))"
+		return "e.e.EncodeInt(int64(" + vname + "))"
 	case "string":
-		return "if e.h.StringToRaw { ee.EncodeStringBytesRaw(bytesView(" + vname + ")) " +
-			"} else { ee.EncodeStringEnc(cUTF8, " + vname + ") }"
+		return "if e.h.StringToRaw { e.e.EncodeStringBytesRaw(bytesView(" + vname + ")) " +
+			"} else { e.e.EncodeStringEnc(cUTF8, " + vname + ") }"
 	case "float32":
-		return "ee.EncodeFloat32(" + vname + ")"
+		return "e.e.EncodeFloat32(" + vname + ")"
 	case "float64":
-		return "ee.EncodeFloat64(" + vname + ")"
+		return "e.e.EncodeFloat64(" + vname + ")"
 	case "bool":
-		return "ee.EncodeBool(" + vname + ")"
+		return "e.e.EncodeBool(" + vname + ")"
 	// case "symbol":
-	// 	return "ee.EncodeSymbol(" + vname + ")"
+	// 	return "e.e.EncodeSymbol(" + vname + ")"
 	default:
 		return "e.encode(" + vname + ")"
 	}
@@ -1988,36 +1988,36 @@ func genInternalEncCommandAsString(s string, vname string) string {
 func genInternalDecCommandAsString(s string) string {
 	switch s {
 	case "uint":
-		return "uint(chkOvf.UintV(dd.DecodeUint64(), uintBitsize))"
+		return "uint(chkOvf.UintV(d.d.DecodeUint64(), uintBitsize))"
 	case "uint8":
-		return "uint8(chkOvf.UintV(dd.DecodeUint64(), 8))"
+		return "uint8(chkOvf.UintV(d.d.DecodeUint64(), 8))"
 	case "uint16":
-		return "uint16(chkOvf.UintV(dd.DecodeUint64(), 16))"
+		return "uint16(chkOvf.UintV(d.d.DecodeUint64(), 16))"
 	case "uint32":
-		return "uint32(chkOvf.UintV(dd.DecodeUint64(), 32))"
+		return "uint32(chkOvf.UintV(d.d.DecodeUint64(), 32))"
 	case "uint64":
-		return "dd.DecodeUint64()"
+		return "d.d.DecodeUint64()"
 	case "uintptr":
-		return "uintptr(chkOvf.UintV(dd.DecodeUint64(), uintBitsize))"
+		return "uintptr(chkOvf.UintV(d.d.DecodeUint64(), uintBitsize))"
 	case "int":
-		return "int(chkOvf.IntV(dd.DecodeInt64(), intBitsize))"
+		return "int(chkOvf.IntV(d.d.DecodeInt64(), intBitsize))"
 	case "int8":
-		return "int8(chkOvf.IntV(dd.DecodeInt64(), 8))"
+		return "int8(chkOvf.IntV(d.d.DecodeInt64(), 8))"
 	case "int16":
-		return "int16(chkOvf.IntV(dd.DecodeInt64(), 16))"
+		return "int16(chkOvf.IntV(d.d.DecodeInt64(), 16))"
 	case "int32":
-		return "int32(chkOvf.IntV(dd.DecodeInt64(), 32))"
+		return "int32(chkOvf.IntV(d.d.DecodeInt64(), 32))"
 	case "int64":
-		return "dd.DecodeInt64()"
+		return "d.d.DecodeInt64()"
 
 	case "string":
-		return "dd.DecodeString()"
+		return "d.d.DecodeString()"
 	case "float32":
-		return "float32(chkOvf.Float32V(dd.DecodeFloat64()))"
+		return "float32(chkOvf.Float32V(d.d.DecodeFloat64()))"
 	case "float64":
-		return "dd.DecodeFloat64()"
+		return "d.d.DecodeFloat64()"
 	case "bool":
-		return "dd.DecodeBool()"
+		return "d.d.DecodeBool()"
 	default:
 		panic(errors.New("gen internal: unknown type for decode: " + s))
 	}

+ 1 - 1
codec/simple.go

@@ -633,7 +633,7 @@ func (h *SimpleHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (e
 	return h.SetExt(rt, tag, &extWrapper{ext, interfaceExtFailer{}})
 }
 
-func (h *SimpleHandle) hasElemSeparators() bool { return true } // as it implements Write(Map|Array)XXX
+// func (h *SimpleHandle) hasElemSeparators() bool { return true } // as it implements Write(Map|Array)XXX
 
 func (h *SimpleHandle) newEncDriver(e *Encoder) encDriver {
 	return &simpleEncDriver{e: e, w: e.w, h: h}

Some files were not shown because too many files changed in this diff