From: Martin Möhrmann Date: Tue, 1 May 2018 09:39:17 +0000 (+0200) Subject: strconv: use bounded bits.TrailingZeros instead of shifts table X-Git-Tag: go1.11beta1~584 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d29ec40e19fa0548292bb0bf2a4fb88920838877;p=gostls13.git strconv: use bounded bits.TrailingZeros instead of shifts table The strconv shifts table is 320 bytes (amd64) and is present in many binaries since integer formatting is very common. Instead of using a precalculated table with shift amounts use a bounded bits.TrailingZeros to determine the shift amount to format numbers in a base that is a power of 2. amd64: name old time/op new time/op delta AppendUint 379ns ± 1% 286ns ± 2% -24.62% (p=0.000 n=20+19) Change-Id: Ib94d9b033321b41e975868943c7fcd9428c5111e Reviewed-on: https://go-review.googlesource.com/110478 Reviewed-by: Brad Fitzpatrick --- diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 817984c3e2..5fcfcb8b83 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -64,7 +64,7 @@ var pkgDeps = map[string][]string{ "math/bits": {}, "math/cmplx": {"math"}, "math/rand": {"L0", "math"}, - "strconv": {"L0", "unicode/utf8", "math"}, + "strconv": {"L0", "unicode/utf8", "math", "math/bits"}, "unicode/utf16": {}, "unicode/utf8": {}, diff --git a/src/strconv/itoa.go b/src/strconv/itoa.go index 78527c8ae6..394716ccd7 100644 --- a/src/strconv/itoa.go +++ b/src/strconv/itoa.go @@ -4,6 +4,8 @@ package strconv +import "math/bits" + const fastSmalls = true // enable fast path for small integers // FormatUint returns the string representation of i in the given base, @@ -79,14 +81,6 @@ const host32bit = ^uint(0)>>32 == 0 const digits = "0123456789abcdefghijklmnopqrstuvwxyz" -var shifts = [len(digits) + 1]uint{ - 1 << 1: 1, - 1 << 2: 2, - 1 << 3: 3, - 1 << 4: 4, - 1 << 5: 5, -} - // formatBits computes the string representation of u in the given base. // If neg is set, u is treated as negative int64 value. If append_ is // set, the string is appended to dst and the resulting byte slice is @@ -158,14 +152,17 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s a[i] = smallsString[is] } - } else if s := shifts[base]; s > 0 { - // base is power of 2: use shifts and masks instead of / and % + } else if isPowerOfTwo(base) { + // It is known that base is a power of two and + // 2 <= base <= len(digits). + // Use shifts and masks instead of / and %. + shift := uint(bits.TrailingZeros(uint(base))) & 31 b := uint64(base) - m := uint(base) - 1 // == 1<= b { i-- a[i] = digits[uint(u)&m] - u >>= s + u >>= shift } // u < base i-- @@ -200,3 +197,7 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s s = string(a[i:]) return } + +func isPowerOfTwo(x int) bool { + return x&(x-1) == 0 +}