1
0
mirror of https://github.com/golang/go synced 2024-11-26 23:01:23 -07:00

strconv: optimize decimal to string conversion

Avoid the decimal lookup in digits array and compute the decimal character value directly.
Reduce calls to 64bit division on 32bit plattforms by splitting conversion into smaller blocks.
Convert value to uintptr type when it can be represented by uintptr.

on darwin/386

benchmark               old ns/op     new ns/op     delta
BenchmarkFormatInt      8352          7466          -10.61%
BenchmarkAppendInt      4281          3401          -20.56%
BenchmarkFormatUint     2785          2251          -19.17%
BenchmarkAppendUint     1770          1223          -30.90%

on darwin/amd64

benchmark               old ns/op     new ns/op     delta
BenchmarkFormatInt      5531          5492          -0.71%
BenchmarkAppendInt      2435          2295          -5.75%
BenchmarkFormatUint     1628          1569          -3.62%
BenchmarkAppendUint     726           750           +3.31%

Change-Id: Ifca281cbdd62ab7d7bd4b077a96da99eb12cf209
Reviewed-on: https://go-review.googlesource.com/2105
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Martin Möhrmann 2014-12-25 19:30:53 +01:00 committed by Robert Griesemer
parent fcd61eb07e
commit 241583a432

View File

@ -40,9 +40,7 @@ func AppendUint(dst []byte, i uint64, base int) []byte {
}
const (
digits = "0123456789abcdefghijklmnopqrstuvwxyz"
digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
digits = "0123456789abcdefghijklmnopqrstuvwxyz"
)
var shifts = [len(digits) + 1]uint{
@ -74,23 +72,34 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s
// convert bits
if base == 10 {
// common case: use constants for / and % because
// the compiler can optimize it into a multiply+shift,
// and unroll loop
for u >= 100 {
i -= 2
q := u / 100
j := uintptr(u - q*100)
a[i+1] = digits01[j]
a[i+0] = digits10[j]
u = q
// common case: use constants for / because
// the compiler can optimize it into a multiply+shift
if ^uintptr(0)>>32 == 0 {
for u > uint64(^uintptr(0)) {
q := u / 1e9
us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr
for j := 9; j > 0; j-- {
i--
qs := us / 10
a[i] = byte(us - qs*10 + '0')
us = qs
}
u = q
}
}
if u >= 10 {
// u guaranteed to fit into a uintptr
us := uintptr(u)
for us >= 10 {
i--
q := u / 10
a[i] = digits[uintptr(u-q*10)]
u = q
q := us / 10
a[i] = byte(us - q*10 + '0')
us = q
}
// u < 10
i--
a[i] = byte(us + '0')
} else if s := shifts[base]; s > 0 {
// base is power of 2: use shifts and masks instead of / and %
@ -101,21 +110,24 @@ func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s
a[i] = digits[uintptr(u)&m]
u >>= s
}
// u < base
i--
a[i] = digits[uintptr(u)]
} else {
// general case
b := uint64(base)
for u >= b {
i--
a[i] = digits[uintptr(u%b)]
u /= b
q := u / b
a[i] = digits[uintptr(u-q*b)]
u = q
}
// u < base
i--
a[i] = digits[uintptr(u)]
}
// u < base
i--
a[i] = digits[uintptr(u)]
// add sign, if any
if neg {
i--