Kaynağa Gözat

internal/encoding/json: improve Value.Int,Uint by reducing allocations

parseNumber does not need to construct new slices for numberParts, it
simply needs to reference the correct subset from the input.

normalizeToString may need to allocate but only if there's a positive
exponent.

name      old time/op    new time/op    delta
Float-4      308ns ± 0%     291ns ± 0%   ~     (p=1.000 n=1+1)
Int-4        498ns ± 0%     341ns ± 0%   ~     (p=1.000 n=1+1)
String-4     262ns ± 0%     250ns ± 0%   ~     (p=1.000 n=1+1)
Bool-4       212ns ± 0%     210ns ± 0%   ~     (p=1.000 n=1+1)

name      old alloc/op   new alloc/op   delta
Float-4      48.0B ± 0%     48.0B ± 0%   ~     (all equal)
Int-4         160B ± 0%       99B ± 0%   ~     (p=1.000 n=1+1)
String-4      176B ± 0%      176B ± 0%   ~     (all equal)
Bool-4       0.00B          0.00B        ~     (all equal)

name      old allocs/op  new allocs/op  delta
Float-4       1.00 ± 0%      1.00 ± 0%   ~     (all equal)
Int-4         9.00 ± 0%      4.00 ± 0%   ~     (p=1.000 n=1+1)
String-4      3.00 ± 0%      3.00 ± 0%   ~     (all equal)
Bool-4        0.00           0.00        ~     (all equal)

Change-Id: If083e18a5914b15e794d34722cbb6539cbd73a53
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170788
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Herbie Ong 6 yıl önce
ebeveyn
işleme
8fa64d98d8
1 değiştirilmiş dosya ile 27 ekleme ve 15 silme
  1. 27 15
      internal/encoding/json/number.go

+ 27 - 15
internal/encoding/json/number.go

@@ -127,7 +127,7 @@ type numberParts struct {
 
 
 // parseNumber constructs numberParts from given []byte. The logic here is
 // parseNumber constructs numberParts from given []byte. The logic here is
 // similar to consumeNumber above with the difference of having to construct
 // similar to consumeNumber above with the difference of having to construct
-// numberParts.
+// numberParts. The slice fields in numberParts are subslices of the input.
 func parseNumber(input []byte) (numberParts, bool) {
 func parseNumber(input []byte) (numberParts, bool) {
 	var neg bool
 	var neg bool
 	var intp []byte
 	var intp []byte
@@ -155,12 +155,14 @@ func parseNumber(input []byte) (numberParts, bool) {
 		s = s[1:]
 		s = s[1:]
 
 
 	case '1' <= s[0] && s[0] <= '9':
 	case '1' <= s[0] && s[0] <= '9':
-		intp = append(intp, s[0])
+		intp = s
+		n := 1
 		s = s[1:]
 		s = s[1:]
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
-			intp = append(intp, s[0])
 			s = s[1:]
 			s = s[1:]
+			n++
 		}
 		}
+		intp = intp[:n]
 
 
 	default:
 	default:
 		return numberParts{}, false
 		return numberParts{}, false
@@ -168,29 +170,34 @@ func parseNumber(input []byte) (numberParts, bool) {
 
 
 	// . followed by 1 or more digits.
 	// . followed by 1 or more digits.
 	if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
 	if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
-		frac = append(frac, s[1])
+		frac = s[1:]
+		n := 1
 		s = s[2:]
 		s = s[2:]
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
-			frac = append(frac, s[0])
 			s = s[1:]
 			s = s[1:]
+			n++
 		}
 		}
+		frac = frac[:n]
 	}
 	}
 
 
 	// e or E followed by an optional - or + and
 	// e or E followed by an optional - or + and
 	// 1 or more digits.
 	// 1 or more digits.
 	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
 	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
 		s = s[1:]
 		s = s[1:]
+		exp = s
+		n := 0
 		if s[0] == '+' || s[0] == '-' {
 		if s[0] == '+' || s[0] == '-' {
-			exp = append(exp, s[0])
 			s = s[1:]
 			s = s[1:]
+			n++
 			if len(s) == 0 {
 			if len(s) == 0 {
 				return numberParts{}, false
 				return numberParts{}, false
 			}
 			}
 		}
 		}
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
 		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
-			exp = append(exp, s[0])
 			s = s[1:]
 			s = s[1:]
+			n++
 		}
 		}
+		exp = exp[:n]
 	}
 	}
 
 
 	return numberParts{
 	return numberParts{
@@ -205,8 +212,7 @@ func parseNumber(input []byte) (numberParts, bool) {
 // E-notation for given numberParts. It will return false if it is not an
 // E-notation for given numberParts. It will return false if it is not an
 // integer or if the exponent exceeds than max/min int value.
 // integer or if the exponent exceeds than max/min int value.
 func normalizeToIntString(n numberParts) (string, bool) {
 func normalizeToIntString(n numberParts) (string, bool) {
-	num := n.intp
-	intpSize := len(num)
+	intpSize := len(n.intp)
 	fracSize := len(n.frac)
 	fracSize := len(n.frac)
 
 
 	if intpSize == 0 && fracSize == 0 {
 	if intpSize == 0 && fracSize == 0 {
@@ -222,16 +228,19 @@ func normalizeToIntString(n numberParts) (string, bool) {
 		exp = int(i)
 		exp = int(i)
 	}
 	}
 
 
+	var num []byte
 	if exp >= 0 {
 	if exp >= 0 {
 		// For positive E, shift fraction digits into integer part and also pad
 		// For positive E, shift fraction digits into integer part and also pad
 		// with zeroes as needed.
 		// with zeroes as needed.
 
 
-		// If there are more digits in fraction than the E value, then number is
-		// not an integer.
+		// If there are more digits in fraction than the E value, then the
+		// number is not an integer.
 		if fracSize > exp {
 		if fracSize > exp {
 			return "", false
 			return "", false
 		}
 		}
 
 
+		// Set cap to make a copy of integer part when appended.
+		num = n.intp[:len(n.intp):len(n.intp)]
 		num = append(num, n.frac...)
 		num = append(num, n.frac...)
 		for i := 0; i < exp-fracSize; i++ {
 		for i := 0; i < exp-fracSize; i++ {
 			num = append(num, '0')
 			num = append(num, '0')
@@ -240,18 +249,21 @@ func normalizeToIntString(n numberParts) (string, bool) {
 	} else {
 	} else {
 		// For negative E, shift digits in integer part out.
 		// For negative E, shift digits in integer part out.
 
 
-		// If there are any fractions to begin with, then number is not an
-		// integer.
+		// If there are fractions, then the number is not an integer.
 		if fracSize > 0 {
 		if fracSize > 0 {
 			return "", false
 			return "", false
 		}
 		}
 
 
+		// index is where the decimal point will be after adjusting for negative
+		// exponent.
 		index := intpSize + exp
 		index := intpSize + exp
 		if index < 0 {
 		if index < 0 {
 			return "", false
 			return "", false
 		}
 		}
-		// If any of the digits being shifted out is non-zero, then number is
-		// not an integer.
+
+		num = n.intp
+		// If any of the digits being shifted to the right of the decimal point
+		// is non-zero, then the number is not an integer.
 		for i := index; i < intpSize; i++ {
 		for i := index; i < intpSize; i++ {
 			if num[i] != '0' {
 			if num[i] != '0' {
 				return "", false
 				return "", false