From 06ed8f0df76e9b0256de286392d42d1ea7dec809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Mo=CC=88hrmann?= Date: Tue, 6 Jan 2015 19:48:31 +0100 Subject: [PATCH] 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/2460 Reviewed-by: Robert Griesemer --- src/strconv/atoi.go | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/strconv/atoi.go b/src/strconv/atoi.go index 9ecec5a58b..965e3a218f 100644 --- a/src/strconv/atoi.go +++ b/src/strconv/atoi.go @@ -36,13 +36,7 @@ const intSize = 32 << (^uint(0) >> 63) // IntSize is the size in bits of an int or uint value. const IntSize = intSize -// Return the first number n such that n*base >= 1<<64. -func cutoff64(base int) uint64 { - if base < 2 { - return 0 - } - return (1<<64-1)/uint64(base) + 1 -} +const maxUint64 = (1<<64 - 1) // ParseUint is like ParseInt but for unsigned numbers. 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) } - s0 := s + i := 0 switch { case len(s) < 1: err = ErrSyntax @@ -65,14 +59,15 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { // Look for octal, hex prefix. switch { case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): - base = 16 - s = s[2:] - if len(s) < 1 { + if len(s) < 3 { err = ErrSyntax goto Error } + base = 16 + i = 2 case s[0] == '0': base = 8 + i = 1 default: base = 10 } @@ -82,11 +77,20 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { goto Error } - n = 0 - cutoff = cutoff64(base) + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // 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<= base { + if v >= byte(base) { n = 0 err = ErrSyntax goto Error @@ -109,7 +113,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { if n >= cutoff { // n*base overflows - n = 1<<64 - 1 + n = maxUint64 err = ErrRange goto Error } @@ -118,7 +122,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { n1 := n + uint64(v) if n1 < n || n1 > maxVal { // n+v overflows - n = 1<<64 - 1 + n = maxUint64 err = ErrRange goto Error } @@ -128,7 +132,7 @@ func ParseUint(s string, base int, bitSize int) (n uint64, err error) { return n, nil 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