]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: speed up floating point print, clean up some code
authorRob Pike <r@golang.org>
Thu, 15 Dec 2011 20:52:29 +0000 (12:52 -0800)
committerRob Pike <r@golang.org>
Thu, 15 Dec 2011 20:52:29 +0000 (12:52 -0800)
%g down to two mallocs from four. Also a mild speedup.

fmt_test.BenchmarkSprintfFloat         3016         2703  -10.38%

Fixes #2557.

R=rsc
CC=golang-dev
https://golang.org/cl/5491054

src/pkg/fmt/format.go
src/pkg/fmt/print.go

index 5f62c067f06a716bd075bf0f39464fb0ecd2c361..78d9e998b1fe0c32c5d600c02ac6fe114c15ba9d 100644 (file)
@@ -154,12 +154,17 @@ func putint(buf []byte, base, val uint64, digits string) int {
        return i - 1
 }
 
+var (
+       trueBytes  = []byte("true")
+       falseBytes = []byte("false")
+)
+
 // fmt_boolean formats a boolean.
 func (f *fmt) fmt_boolean(v bool) {
        if v {
-               f.padString("true")
+               f.pad(trueBytes)
        } else {
-               f.padString("false")
+               f.pad(falseBytes)
        }
 }
 
@@ -283,31 +288,18 @@ func (f *fmt) fmt_s(s string) {
 }
 
 // fmt_sx formats a string as a hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sx(s string) {
-       t := ""
+func (f *fmt) fmt_sx(s, digits string) {
+       // TODO: Avoid buffer by pre-padding.
+       var b bytes.Buffer
        for i := 0; i < len(s); i++ {
                if i > 0 && f.space {
-                       t += " "
+                       b.WriteByte(' ')
                }
                v := s[i]
-               t += string(ldigits[v>>4])
-               t += string(ldigits[v&0xF])
+               b.WriteByte(digits[v>>4])
+               b.WriteByte(digits[v&0xF])
        }
-       f.padString(t)
-}
-
-// fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sX(s string) {
-       t := ""
-       for i := 0; i < len(s); i++ {
-               if i > 0 && f.space {
-                       t += " "
-               }
-               v := s[i]
-               t += string(udigits[v>>4])
-               t += string(udigits[v&0xF])
-       }
-       f.padString(t)
+       f.pad(b.Bytes())
 }
 
 // fmt_q formats a string as a double-quoted, escaped Go string constant.
@@ -329,13 +321,13 @@ func (f *fmt) fmt_q(s string) {
 // fmt_qc formats the integer as a single-quoted, escaped Go character constant.
 // If the character is not valid Unicode, it will print '\ufffd'.
 func (f *fmt) fmt_qc(c int64) {
-       var quoted string
+       var quoted []byte
        if f.plus {
-               quoted = strconv.QuoteRuneToASCII(rune(c))
+               quoted = strconv.AppendQuoteRuneToASCII(f.intbuf[0:0], rune(c))
        } else {
-               quoted = strconv.QuoteRune(rune(c))
+               quoted = strconv.AppendQuoteRune(f.intbuf[0:0], rune(c))
        }
-       f.padString(quoted)
+       f.pad(quoted)
 }
 
 // floating-point
@@ -347,57 +339,70 @@ func doPrec(f *fmt, def int) int {
        return def
 }
 
-// Add a plus sign or space to the floating-point string representation if missing and required.
-func (f *fmt) plusSpace(s string) {
-       if s[0] != '-' {
+// formatFloat formats a float64; it is an efficient equivalent to  f.pad(strconv.FormatFloat()...).
+func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
+       // We leave one byte at the beginning of f.intbuf for a sign if needed,
+       // and make it a space, which we might be able to use.
+       f.intbuf[0] = ' '
+       slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
+       // Add a plus sign or space to the floating-point string representation if missing and required.
+       // The formatted number starts at slice[1].
+       switch slice[1] {
+       case '-', '+':
+               // We're set; drop the leading space.
+               slice = slice[1:]
+       default:
+               // There's no sign, but we might need one.
                if f.plus {
-                       s = "+" + s
+                       slice[0] = '+'
                } else if f.space {
-                       s = " " + s
+                       // space is already there
+               } else {
+                       slice = slice[1:]
                }
        }
-       f.padString(s)
+       f.pad(slice)
 }
 
 // fmt_e64 formats a float64 in the form -1.23e+12.
-func (f *fmt) fmt_e64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'e', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_e64(v float64) { f.formatFloat(v, 'e', doPrec(f, 6), 64) }
 
 // fmt_E64 formats a float64 in the form -1.23E+12.
-func (f *fmt) fmt_E64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'E', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_E64(v float64) { f.formatFloat(v, 'E', doPrec(f, 6), 64) }
 
 // fmt_f64 formats a float64 in the form -1.23.
-func (f *fmt) fmt_f64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'f', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) }
 
 // fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
-func (f *fmt) fmt_g64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'g', doPrec(f, -1), 64)) }
+func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) }
 
 // fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
-func (f *fmt) fmt_G64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'G', doPrec(f, -1), 64)) }
+func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) }
 
 // fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
-func (f *fmt) fmt_fb64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'b', 0, 64)) }
+func (f *fmt) fmt_fb64(v float64) { f.formatFloat(v, 'b', 0, 64) }
 
 // float32
 // cannot defer to float64 versions
 // because it will get rounding wrong in corner cases.
 
 // fmt_e32 formats a float32 in the form -1.23e+12.
-func (f *fmt) fmt_e32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'e', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_e32(v float32) { f.formatFloat(float64(v), 'e', doPrec(f, 6), 32) }
 
 // fmt_E32 formats a float32 in the form -1.23E+12.
-func (f *fmt) fmt_E32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'E', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_E32(v float32) { f.formatFloat(float64(v), 'E', doPrec(f, 6), 32) }
 
 // fmt_f32 formats a float32 in the form -1.23.
-func (f *fmt) fmt_f32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'f', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_f32(v float32) { f.formatFloat(float64(v), 'f', doPrec(f, 6), 32) }
 
 // fmt_g32 formats a float32 in the 'f' or 'e' form according to size.
-func (f *fmt) fmt_g32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'g', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_g32(v float32) { f.formatFloat(float64(v), 'g', doPrec(f, -1), 32) }
 
 // fmt_G32 formats a float32 in the 'f' or 'E' form according to size.
-func (f *fmt) fmt_G32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'G', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_G32(v float32) { f.formatFloat(float64(v), 'G', doPrec(f, -1), 32) }
 
 // fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
-func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.FormatFloat(float64(v), 'b', 0, 32)) }
+func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
 
 // fmt_c64 formats a complex64 according to the verb.
 func (f *fmt) fmt_c64(v complex64, verb rune) {
index 9f157daaee01d043890900818df3056f4d9d1534..3b7d3464e209a215e4d65fa7e88bca721e218c21 100644 (file)
@@ -503,9 +503,9 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) {
        case 's':
                p.fmt.fmt_s(v)
        case 'x':
-               p.fmt.fmt_sx(v)
+               p.fmt.fmt_sx(v, ldigits)
        case 'X':
-               p.fmt.fmt_sX(v)
+               p.fmt.fmt_sx(v, udigits)
        case 'q':
                p.fmt.fmt_q(v)
        default:
@@ -542,9 +542,9 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, depth int) {
        case 's':
                p.fmt.fmt_s(s)
        case 'x':
-               p.fmt.fmt_sx(s)
+               p.fmt.fmt_sx(s, ldigits)
        case 'X':
-               p.fmt.fmt_sX(s)
+               p.fmt.fmt_sx(s, udigits)
        case 'q':
                p.fmt.fmt_q(s)
        default: