1
0
mirror of https://github.com/golang/go synced 2024-09-29 11:34:32 -06:00

strconv: Speed improvement to number parsing

Run underscore validation only if we have seen underscores.

Some performance results on my laptop:
name                   old time/op  new time/op  delta
Atof64Decimal-12       30.5ns ± 0%  23.8ns ± 0%  -22.02%  (p=0.016 n=5+4)
Atof64Float-12         39.0ns ± 0%  28.7ns ± 0%  -26.39%  (p=0.002 n=6+6)
Atof64FloatExp-12      64.4ns ± 1%  54.4ns ± 1%  -15.65%  (p=0.002 n=6+6)
Atof64Big-12            115ns ± 1%    87ns ± 1%  -24.45%  (p=0.002 n=6+6)
Atof64RandomBits-12     187ns ±14%   156ns ±19%  -16.46%  (p=0.032 n=6+6)
Atof64RandomFloats-12   126ns ± 0%   105ns ± 1%  -16.65%  (p=0.000 n=6+5)
Atof32Decimal-12       32.0ns ± 1%  24.0ns ± 1%  -24.97%  (p=0.002 n=6+6)
Atof32Float-12         37.1ns ± 1%  27.0ns ± 1%  -27.42%  (p=0.002 n=6+6)
Atof32FloatExp-12      68.4ns ± 1%  54.2ns ± 1%  -20.77%  (p=0.002 n=6+6)
Atof32Random-12        92.0ns ± 1%  77.4ns ± 0%  -15.81%  (p=0.000 n=6+5)
ParseInt/Pos/7bit-12   19.4ns ± 1%  13.8ns ±10%  -28.94%  (p=0.002 n=6+6)
ParseInt/Pos/26bit-12  29.1ns ± 1%  19.8ns ± 2%  -31.92%  (p=0.002 n=6+6)
ParseInt/Pos/31bit-12  33.1ns ± 0%  22.3ns ± 3%  -32.62%  (p=0.004 n=5+6)
ParseInt/Pos/56bit-12  47.8ns ± 1%  30.7ns ± 1%  -35.78%  (p=0.004 n=6+5)
ParseInt/Pos/63bit-12  51.9ns ± 1%  33.4ns ± 2%  -35.49%  (p=0.002 n=6+6)
ParseInt/Neg/7bit-12   18.5ns ± 4%  13.4ns ± 3%  -27.88%  (p=0.002 n=6+6)
ParseInt/Neg/26bit-12  28.4ns ± 3%  19.7ns ± 3%  -30.38%  (p=0.002 n=6+6)
ParseInt/Neg/31bit-12  31.9ns ± 1%  21.8ns ± 2%  -31.56%  (p=0.002 n=6+6)
ParseInt/Neg/56bit-12  46.2ns ± 0%  30.6ns ± 1%  -33.73%  (p=0.004 n=5+6)
ParseInt/Neg/63bit-12  50.2ns ± 1%  33.2ns ± 1%  -33.96%  (p=0.002 n=6+6)

Fixes #33330

Change-Id: I119da66457c2fbaf6e88bb90cf56417a16df8f0e
Reviewed-on: https://go-review.googlesource.com/c/go/+/187957
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Sam Arnold 2019-08-10 12:06:50 -04:00 committed by Robert Griesemer
parent 22355d6cd2
commit 3d48ae355b
2 changed files with 31 additions and 15 deletions

View File

@ -84,7 +84,7 @@ func (b *decimal) set(s string) (ok bool) {
for ; i < len(s); i++ {
switch {
case s[i] == '_':
// underscoreOK already called
// readFloat already checked underscores
continue
case s[i] == '.':
if sawdot {
@ -140,7 +140,7 @@ func (b *decimal) set(s string) (ok bool) {
e := 0
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
if s[i] == '_' {
// underscoreOK already called
// readFloat already checked underscores
continue
}
if e < 10000 {
@ -159,10 +159,11 @@ func (b *decimal) set(s string) (ok bool) {
}
// readFloat reads a decimal mantissa and exponent from a float
// string representation. It returns ok==false if the number could
// not fit return types or is invalid.
// string representation. It returns ok==false if the number
// is invalid.
func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
i := 0
underscores := false
// optional sign
if i >= len(s) {
@ -195,7 +196,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
for ; i < len(s); i++ {
switch c := s[i]; true {
case c == '_':
// underscoreOK already called
underscores = true
continue
case c == '.':
@ -271,7 +272,7 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
e := 0
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i++ {
if s[i] == '_' {
// underscoreOK already called
underscores = true
continue
}
if e < 10000 {
@ -291,6 +292,11 @@ func readFloat(s string) (mantissa uint64, exp int, neg, trunc, hex, ok bool) {
if mantissa != 0 {
exp = dp - ndMant
}
if underscores && !underscoreOK(s) {
return
}
ok = true
return
}
@ -554,12 +560,16 @@ func atof32(s string) (f float32, err error) {
}
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
if hex && ok {
if !ok {
return 0, syntaxError(fnParseFloat, s)
}
if hex {
f, err := atofHex(s, &float32info, mantissa, exp, neg, trunc)
return float32(f), err
}
if optimize && ok {
if optimize {
// Try pure floating-point arithmetic conversion.
if !trunc {
if f, ok := atof32exact(mantissa, exp, neg); ok {
@ -597,11 +607,15 @@ func atof64(s string) (f float64, err error) {
}
mantissa, exp, neg, trunc, hex, ok := readFloat(s)
if hex && ok {
if !ok {
return 0, syntaxError(fnParseFloat, s)
}
if hex {
return atofHex(s, &float64info, mantissa, exp, neg, trunc)
}
if optimize && ok {
if optimize {
// Try pure floating-point arithmetic conversion.
if !trunc {
if f, ok := atof64exact(mantissa, exp, neg); ok {
@ -658,9 +672,6 @@ func atof64(s string) (f float64, err error) {
// ParseFloat recognizes the strings "NaN", "+Inf", and "-Inf" as their
// respective special floating point values. It ignores case when matching.
func ParseFloat(s string, bitSize int) (float64, error) {
if !underscoreOK(s) {
return 0, syntaxError(fnParseFloat, s)
}
if bitSize == 32 {
f, err := atof32(s)
return float64(f), err

View File

@ -58,7 +58,7 @@ const maxUint64 = 1<<64 - 1
func ParseUint(s string, base int, bitSize int) (uint64, error) {
const fnParseUint = "ParseUint"
if s == "" || !underscoreOK(s) {
if s == "" {
return 0, syntaxError(fnParseUint, s)
}
@ -113,12 +113,13 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
maxVal := uint64(1)<<uint(bitSize) - 1
underscores := false
var n uint64
for _, c := range []byte(s) {
var d byte
switch {
case c == '_' && base0:
// underscoreOK already called
underscores = true
continue
case '0' <= c && c <= '9':
d = c - '0'
@ -146,6 +147,10 @@ func ParseUint(s string, base int, bitSize int) (uint64, error) {
n = n1
}
if underscores && !underscoreOK(s0) {
return 0, syntaxError(fnParseUint, s0)
}
return n, nil
}