1
0
mirror of https://github.com/golang/go synced 2024-11-21 15:54:43 -07:00

strconv: 34% to 63% faster conversions

(Note that the Int and Uint benchmarks use different test sets
and thus cannot be compared against each other. Int and Uint
conversions are approximately the same speed).

Before (best of 3 runs):
strconv_test.BenchmarkFormatInt    100000    15636 ns/op
strconv_test.BenchmarkAppendInt    100000    18930 ns/op
strconv_test.BenchmarkFormatUint   500000     4392 ns/op
strconv_test.BenchmarkAppendUint   500000     5152 ns/op

After (best of 3 runs):

strconv_test.BenchmarkFormatInt    200000    10070 ns/op (-36%)
strconv_test.BenchmarkAppendInt    200000     7097 ns/op (-63%)
strconv_test.BenchmarkFormatUint  1000000     2893 ns/op (-34%)
strconv_test.BenchmarkAppendUint   500000     2462 ns/op (-52%)

R=r, rsc, r
CC=golang-dev
https://golang.org/cl/5449093
This commit is contained in:
Robert Griesemer 2011-12-06 08:15:45 -08:00
parent 46deaa297b
commit e0c006a9b0
2 changed files with 126 additions and 31 deletions

View File

@ -6,37 +6,14 @@ package strconv
// FormatUint returns the string representation of i in the given base.
func FormatUint(i uint64, base int) string {
u := i
if base < 2 || 36 < base {
panic("invalid base " + Itoa(base))
}
if u == 0 {
return "0"
}
// Assemble decimal in reverse order.
var buf [64]byte
j := len(buf)
b := uint64(base)
for u > 0 {
j--
buf[j] = "0123456789abcdefghijklmnopqrstuvwxyz"[u%b]
u /= b
}
return string(buf[j:])
_, s := formatBits(nil, i, base, false, false)
return s
}
// FormatInt returns the string representation of i in the given base.
func FormatInt(i int64, base int) string {
if i == 0 {
return "0"
}
if i < 0 {
return "-" + FormatUint(-uint64(i), base)
}
return FormatUint(uint64(i), base)
_, s := formatBits(nil, uint64(i), base, true, false)
return s
}
// Itoa is shorthand for FormatInt(i, 10).
@ -47,11 +24,95 @@ func Itoa(i int) string {
// AppendInt appends the string form of the integer i,
// as generated by FormatInt, to dst and returns the extended buffer.
func AppendInt(dst []byte, i int64, base int) []byte {
return append(dst, FormatInt(i, base)...)
dst, _ = formatBits(dst, uint64(i), base, true, true)
return dst
}
// AppendUint appends the string form of the unsigned integer i,
// as generated by FormatUint, to dst and returns the extended buffer.
func AppendUint(dst []byte, i uint64, base int) []byte {
return append(dst, FormatUint(i, base)...)
dst, _ = formatBits(dst, i, base, false, true)
return dst
}
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 signed is set, u is treated as int64 value. If append_ is set, the
// string is appended to dst and the resulting byte slice is returned as
// the first result value; otherwise the string is simply returned as the
// second result value.
//
func formatBits(dst []byte, u uint64, base int, signed, append_ bool) (d []byte, s string) {
if base < 2 || base > len(digits) {
panic("invalid base")
}
// 2 <= base && base <= len(digits)
if u == 0 {
if append_ {
d = append(dst, '0')
return
}
s = "0"
return
}
var a [64 + 1]byte // +1 for sign of 64bit value in base 2
i := len(a)
x := int64(u)
if x < 0 && signed {
u = -u
}
// convert bits
if base == 10 {
// common case: use constant 10 for / and % because
// the compiler can optimize it into a multiply+shift
for u != 0 {
i--
a[i] = digits[u%10]
u /= 10
}
} else if s := shifts[base]; s > 0 {
// base is power of 2: use shifts and masks instead of / and %
m := uintptr(1)<<s - 1
for u != 0 {
i--
a[i] = digits[uintptr(u)&m]
u >>= s
}
} else {
// general case
b := uint64(base)
for u != 0 {
i--
a[i] = digits[u%b]
u /= b
}
}
// add sign, if any
if x < 0 && signed {
i--
a[i] = '-'
}
if append_ {
d = append(dst, a[i:]...)
return
}
s = string(a[i:])
return
}

View File

@ -77,8 +77,8 @@ func TestItoa(t *testing.T) {
t.Errorf("FormatUint(%v, %v) = %v want %v",
test.in, test.base, s, test.out)
}
x := AppendUint([]byte("abc"), uint64(test.in), test.base)
if string(x) != "abc"+test.out {
x := AppendUint(nil, uint64(test.in), test.base)
if string(x) != test.out {
t.Errorf("AppendUint(%q, %v, %v) = %q want %v",
"abc", uint64(test.in), test.base, x, test.out)
}
@ -124,3 +124,37 @@ func TestUitoa(t *testing.T) {
}
}
func BenchmarkFormatInt(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range itob64tests {
FormatInt(test.in, test.base)
}
}
}
func BenchmarkAppendInt(b *testing.B) {
dst := make([]byte, 0, 30)
for i := 0; i < b.N; i++ {
for _, test := range itob64tests {
AppendInt(dst, test.in, test.base)
}
}
}
func BenchmarkFormatUint(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range uitob64tests {
FormatUint(test.in, test.base)
}
}
}
func BenchmarkAppendUint(b *testing.B) {
dst := make([]byte, 0, 30)
for i := 0; i < b.N; i++ {
for _, test := range uitob64tests {
AppendUint(dst, test.in, test.base)
}
}
}