From 0ed5e6a2be4c7248dfb6c870c445e2504f818623 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 15 Nov 2011 12:17:25 -0500 Subject: [PATCH] strconv: make Ftoa faster Make code amenable to escape analysis so that the decimal values do not escape. benchmark old ns/op new ns/op delta strconv_test.BenchmarkAtof64Decimal 229 233 +1.75% strconv_test.BenchmarkAtof64Float 261 263 +0.77% strconv_test.BenchmarkAtof64FloatExp 7760 7757 -0.04% strconv_test.BenchmarkAtof64Big 3086 3053 -1.07% strconv_test.BenchmarkFtoa64Decimal 6866 2629 -61.71% strconv_test.BenchmarkFtoa64Float 7211 3064 -57.51% strconv_test.BenchmarkFtoa64FloatExp 12587 8263 -34.35% strconv_test.BenchmarkFtoa64Big 7058 2825 -59.97% json.BenchmarkCodeEncoder 357355200 276528200 -22.62% json.BenchmarkCodeMarshal 360735200 279646400 -22.48% json.BenchmarkCodeDecoder 731528600 709460600 -3.02% json.BenchmarkCodeUnmarshal 754774400 731051200 -3.14% json.BenchmarkCodeUnmarshalReuse 713379000 704218000 -1.28% json.BenchmarkSkipValue 51594300 51682600 +0.17% benchmark old MB/s new MB/s speedup json.BenchmarkCodeEncoder 5.43 7.02 1.29x json.BenchmarkCodeMarshal 5.38 6.94 1.29x json.BenchmarkCodeDecoder 2.65 2.74 1.03x json.BenchmarkCodeUnmarshal 2.57 2.65 1.03x json.BenchmarkCodeUnmarshalReuse 2.72 2.76 1.01x json.BenchmarkSkipValue 38.61 38.55 1.00x R=golang-dev, r CC=golang-dev https://golang.org/cl/5369111 --- src/pkg/strconv/decimal.go | 27 ++++++++++----------------- src/pkg/strconv/decimal_test.go | 12 +++++++++--- src/pkg/strconv/ftoa.go | 9 ++++++--- src/pkg/strconv/internal_test.go | 6 +++++- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go index f572ea4a22..541553097b 100644 --- a/src/pkg/strconv/decimal.go +++ b/src/pkg/strconv/decimal.go @@ -102,12 +102,6 @@ func (a *decimal) Assign(v uint64) { trim(a) } -func newDecimal(i uint64) *decimal { - a := new(decimal) - a.Assign(i) - return a -} - // Maximum shift that we can do in one pass without overflow. // Signed int has 31 bits, and we have to be able to accommodate 9< 0.1. -func (a *decimal) Round(nd int) *decimal { +func (a *decimal) Round(nd int) { if nd < 0 || nd >= a.nd { - return a + return } if shouldRoundUp(a, nd) { - return a.RoundUp(nd) + a.RoundUp(nd) + } else { + a.RoundDown(nd) } - return a.RoundDown(nd) } // Round a down to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundDown(nd int) *decimal { +func (a *decimal) RoundDown(nd int) { if nd < 0 || nd >= a.nd { - return a + return } a.nd = nd trim(a) - return a } // Round a up to nd digits (or fewer). // Returns receiver for convenience. -func (a *decimal) RoundUp(nd int) *decimal { +func (a *decimal) RoundUp(nd int) { if nd < 0 || nd >= a.nd { - return a + return } // round up @@ -337,7 +331,7 @@ func (a *decimal) RoundUp(nd int) *decimal { if c < '9' { // can stop after this digit a.d[i]++ a.nd = i + 1 - return a + return } } @@ -346,7 +340,6 @@ func (a *decimal) RoundUp(nd int) *decimal { a.d[0] = '1' a.nd = 1 a.dp++ - return a } // Extract integer part, rounded appropriately. diff --git a/src/pkg/strconv/decimal_test.go b/src/pkg/strconv/decimal_test.go index deb2e02f61..13a127f5b2 100644 --- a/src/pkg/strconv/decimal_test.go +++ b/src/pkg/strconv/decimal_test.go @@ -70,17 +70,23 @@ var roundtests = []roundTest{ func TestDecimalRound(t *testing.T) { for i := 0; i < len(roundtests); i++ { test := &roundtests[i] - s := NewDecimal(test.i).RoundDown(test.nd).String() + d := NewDecimal(test.i) + d.RoundDown(test.nd) + s := d.String() if s != test.down { t.Errorf("Decimal %v RoundDown %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).Round(test.nd).String() + d = NewDecimal(test.i) + d.Round(test.nd) + s = d.String() if s != test.round { t.Errorf("Decimal %v Round %d = %v, want %v", test.i, test.nd, s, test.down) } - s = NewDecimal(test.i).RoundUp(test.nd).String() + d = NewDecimal(test.i) + d.RoundUp(test.nd) + s = d.String() if s != test.up { t.Errorf("Decimal %v RoundUp %d = %v, want %v", test.i, test.nd, s, test.up) diff --git a/src/pkg/strconv/ftoa.go b/src/pkg/strconv/ftoa.go index 07fe806b97..8342b6abe7 100644 --- a/src/pkg/strconv/ftoa.go +++ b/src/pkg/strconv/ftoa.go @@ -98,7 +98,8 @@ func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string { // The shift is exp - flt.mantbits because mant is a 1-bit integer // followed by a flt.mantbits fraction, and we are treating it as // a 1+flt.mantbits-bit integer. - d := newDecimal(mant) + d := new(decimal) + d.Assign(mant) d.Shift(exp - int(flt.mantbits)) // Round appropriately. @@ -184,7 +185,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // d = mant << (exp - mantbits) // Next highest floating point number is mant+1 << exp-mantbits. // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1. - upper := newDecimal(mant*2 + 1) + upper := new(decimal) + upper.Assign(mant*2 + 1) upper.Shift(exp - int(flt.mantbits) - 1) // d = mant << (exp - mantbits) @@ -203,7 +205,8 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { mantlo = mant*2 - 1 explo = exp - 1 } - lower := newDecimal(mantlo*2 + 1) + lower := new(decimal) + lower.Assign(mantlo*2 + 1) lower.Shift(explo - int(flt.mantbits) - 1) // The upper and lower bounds are possible outputs only if diff --git a/src/pkg/strconv/internal_test.go b/src/pkg/strconv/internal_test.go index 9a7f4f0867..d0fa80edfb 100644 --- a/src/pkg/strconv/internal_test.go +++ b/src/pkg/strconv/internal_test.go @@ -6,7 +6,11 @@ package strconv -func NewDecimal(i uint64) *decimal { return newDecimal(i) } +func NewDecimal(i uint64) *decimal { + d := new(decimal) + d.Assign(i) + return d +} func SetOptimize(b bool) bool { old := optimize