Commit 06ed8f0d authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Robert Griesemer

strconv: speed up atoi for common cases

Add compile time constants for bases 10 and 16 instead of computing the cutoff
value on every invocation of ParseUint by a division.

Reduce usage of slice operations.

amd64:
benchmark              old ns/op     new ns/op     delta
BenchmarkAtoi          44.6          36.0          -19.28%
BenchmarkAtoiNeg       44.2          38.9          -11.99%
BenchmarkAtoi64        72.5          56.7          -21.79%
BenchmarkAtoi64Neg     66.1          58.6          -11.35%

386:
benchmark              old ns/op     new ns/op     delta
BenchmarkAtoi          86.6          73.0          -15.70%
BenchmarkAtoiNeg       86.6          72.3          -16.51%
BenchmarkAtoi64        126           108           -14.29%
BenchmarkAtoi64Neg     126           108           -14.29%

Change-Id: I0a271132120d776c97bb4ed1099793c73e159893
Reviewed-on: https://go-review.googlesource.com/2460Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent db7fd1c1
...@@ -36,13 +36,7 @@ const intSize = 32 << (^uint(0) >> 63) ...@@ -36,13 +36,7 @@ const intSize = 32 << (^uint(0) >> 63)
// IntSize is the size in bits of an int or uint value. // IntSize is the size in bits of an int or uint value.
const IntSize = intSize const IntSize = intSize
// Return the first number n such that n*base >= 1<<64. const maxUint64 = (1<<64 - 1)
func cutoff64(base int) uint64 {
if base < 2 {
return 0
}
return (1<<64-1)/uint64(base) + 1
}
// ParseUint is like ParseInt but for unsigned numbers. // ParseUint is like ParseInt but for unsigned numbers.
func ParseUint(s string, base int, bitSize int) (n uint64, err error) { func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
...@@ -52,7 +46,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -52,7 +46,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
bitSize = int(IntSize) bitSize = int(IntSize)
} }
s0 := s i := 0
switch { switch {
case len(s) < 1: case len(s) < 1:
err = ErrSyntax err = ErrSyntax
...@@ -65,14 +59,15 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -65,14 +59,15 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
// Look for octal, hex prefix. // Look for octal, hex prefix.
switch { switch {
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
base = 16 if len(s) < 3 {
s = s[2:]
if len(s) < 1 {
err = ErrSyntax err = ErrSyntax
goto Error goto Error
} }
base = 16
i = 2
case s[0] == '0': case s[0] == '0':
base = 8 base = 8
i = 1
default: default:
base = 10 base = 10
} }
...@@ -82,11 +77,20 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -82,11 +77,20 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
goto Error goto Error
} }
n = 0 // Cutoff is the smallest number such that cutoff*base > maxUint64.
cutoff = cutoff64(base) // Use compile-time constants for common cases.
switch base {
case 10:
cutoff = maxUint64/10 + 1
case 16:
cutoff = maxUint64/16 + 1
default:
cutoff = maxUint64/uint64(base) + 1
}
maxVal = 1<<uint(bitSize) - 1 maxVal = 1<<uint(bitSize) - 1
for i := 0; i < len(s); i++ { for ; i < len(s); i++ {
var v byte var v byte
d := s[i] d := s[i]
switch { switch {
...@@ -101,7 +105,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -101,7 +105,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
err = ErrSyntax err = ErrSyntax
goto Error goto Error
} }
if int(v) >= base { if v >= byte(base) {
n = 0 n = 0
err = ErrSyntax err = ErrSyntax
goto Error goto Error
...@@ -109,7 +113,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -109,7 +113,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
if n >= cutoff { if n >= cutoff {
// n*base overflows // n*base overflows
n = 1<<64 - 1 n = maxUint64
err = ErrRange err = ErrRange
goto Error goto Error
} }
...@@ -118,7 +122,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -118,7 +122,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
n1 := n + uint64(v) n1 := n + uint64(v)
if n1 < n || n1 > maxVal { if n1 < n || n1 > maxVal {
// n+v overflows // n+v overflows
n = 1<<64 - 1 n = maxUint64
err = ErrRange err = ErrRange
goto Error goto Error
} }
...@@ -128,7 +132,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { ...@@ -128,7 +132,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) {
return n, nil return n, nil
Error: Error:
return n, &NumError{"ParseUint", s0, err} return n, &NumError{"ParseUint", s, err}
} }
// ParseInt interprets a string s in the given base (2 to 36) and // ParseInt interprets a string s in the given base (2 to 36) and
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment