]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: cleanup and optimize doPrintf for simple formats
authorMartin Möhrmann <martisch@uos.de>
Sun, 20 Mar 2016 14:24:50 +0000 (15:24 +0100)
committerRob Pike <r@golang.org>
Wed, 23 Mar 2016 06:31:12 +0000 (06:31 +0000)
Make a fast path for format strings that do not use
precision or width specifications or argument indices.

Only check and enforce the restriction to not pad left with zeros
in code paths that change either f.minus or f.zero.

Consolidate the if chains at the end of the main doPrintf loop
into a switch statement. Move error printing into extra
functions to reduce size of this switch statement.

name                             old time/op  new time/op  delta
SprintfPadding-2                  234ns ± 1%   233ns ± 1%   -0.54%  (p=0.010 n=19+19)
SprintfEmpty-2                   37.0ns ± 3%  39.1ns ±14%     ~     (p=0.501 n=17+20)
SprintfString-2                   112ns ± 1%   101ns ± 1%   -9.21%  (p=0.000 n=19+20)
SprintfTruncateString-2           139ns ± 1%   139ns ± 0%   +0.57%  (p=0.000 n=19+19)
SprintfQuoteString-2              402ns ± 0%   392ns ± 0%   -2.35%  (p=0.000 n=19+20)
SprintfInt-2                      114ns ± 1%   102ns ± 2%  -10.92%  (p=0.000 n=20+20)
SprintfIntInt-2                   177ns ± 2%   155ns ± 2%  -12.67%  (p=0.000 n=18+18)
SprintfPrefixedInt-2              260ns ± 3%   249ns ± 3%   -4.55%  (p=0.000 n=20+20)
SprintfFloat-2                    190ns ± 1%   178ns ± 2%   -6.54%  (p=0.000 n=20+20)
SprintfComplex-2                  533ns ± 1%   517ns ± 3%   -2.95%  (p=0.000 n=20+20)
SprintfBoolean-2                  102ns ± 1%    93ns ± 2%   -9.30%  (p=0.000 n=20+20)
SprintfHexString-2                176ns ± 0%   168ns ± 2%   -4.49%  (p=0.000 n=16+19)
SprintfHexBytes-2                 181ns ± 1%   174ns ± 2%   -4.27%  (p=0.000 n=20+20)
SprintfBytes-2                    326ns ± 1%   311ns ± 1%   -4.51%  (p=0.000 n=20+20)
ManyArgs-2                        540ns ± 2%   497ns ± 1%   -8.08%  (p=0.000 n=18+16)
FprintInt-2                       150ns ± 0%   149ns ± 0%   -0.33%  (p=0.000 n=20+18)
FprintfBytes-2                    185ns ± 0%   165ns ± 0%  -10.98%  (p=0.000 n=20+18)
FprintIntNoAlloc-2                113ns ± 0%   112ns ± 0%   -0.88%  (p=0.000 n=20+20)

Change-Id: I9ada8faa1f46aa67ea116a94ab3f4ad3e405c8fe
Reviewed-on: https://go-review.googlesource.com/20919
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/fmt/print.go

index 0064ab373472fb6ea2c880f970b30773a77aa6a2..06d3049b8f4ab14d9d2315d2460d7fe89592352f 100644 (file)
@@ -943,11 +943,24 @@ func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum
        return argNum, i + wid, ok
 }
 
+func (p *pp) badArgNum(verb rune) {
+       p.buf.WriteString(percentBangString)
+       p.buf.WriteRune(verb)
+       p.buf.WriteString(badIndexString)
+}
+
+func (p *pp) missingArg(verb rune) {
+       p.buf.WriteString(percentBangString)
+       p.buf.WriteRune(verb)
+       p.buf.WriteString(missingString)
+}
+
 func (p *pp) doPrintf(format string, a []interface{}) {
        end := len(format)
        argNum := 0         // we process one argument per non-trivial format
        afterIndex := false // previous item in format was an index like [3].
        p.reordered = false
+formatLoop:
        for i := 0; i < end; {
                p.goodArgNum = true
                lasti := i
@@ -967,21 +980,40 @@ func (p *pp) doPrintf(format string, a []interface{}) {
 
                // Do we have flags?
                p.fmt.clearflags()
-       F:
+       simpleFormat:
                for ; i < end; i++ {
-                       switch format[i] {
+                       c := format[i]
+                       switch c {
                        case '#':
                                p.fmt.sharp = true
                        case '0':
-                               p.fmt.zero = true
+                               p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left.
                        case '+':
                                p.fmt.plus = true
                        case '-':
                                p.fmt.minus = true
+                               p.fmt.zero = false // Do not pad with zeros to the right.
                        case ' ':
                                p.fmt.space = true
                        default:
-                               break F
+                               // Fast path for common case of ascii lower case simple verbs
+                               // without precision or width or argument indices.
+                               if 'a' <= c && c <= 'z' && argNum < len(a) {
+                                       if c == 'v' {
+                                               // Go syntax
+                                               p.fmt.sharpV = p.fmt.sharp
+                                               p.fmt.sharp = false
+                                               // Struct-field syntax
+                                               p.fmt.plusV = p.fmt.plus
+                                               p.fmt.plus = false
+                                       }
+                                       p.printArg(a[argNum], rune(c))
+                                       argNum++
+                                       i++
+                                       continue formatLoop
+                               }
+                               // Format is more complex than simple flags and a verb or is malformed.
+                               break simpleFormat
                        }
                }
 
@@ -1002,6 +1034,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
                        if p.fmt.wid < 0 {
                                p.fmt.wid = -p.fmt.wid
                                p.fmt.minus = true
+                               p.fmt.zero = false // Do not pad with zeros to the right.
                        }
                        afterIndex = false
                } else {
@@ -1045,47 +1078,31 @@ func (p *pp) doPrintf(format string, a []interface{}) {
 
                if i >= end {
                        p.buf.WriteString(noVerbString)
-                       continue
-               }
-               c, w := utf8.DecodeRuneInString(format[i:])
-               i += w
-               // percent is special - absorbs no operand
-               if c == '%' {
-                       p.buf.WriteByte('%') // We ignore width and prec.
-                       continue
-               }
-               if !p.goodArgNum {
-                       p.buf.WriteString(percentBangString)
-                       p.buf.WriteRune(c)
-                       p.buf.WriteString(badIndexString)
-                       continue
-               } else if argNum >= len(a) { // out of operands
-                       p.buf.WriteString(percentBangString)
-                       p.buf.WriteRune(c)
-                       p.buf.WriteString(missingString)
-                       continue
+                       break
                }
 
-               if c == 'v' {
-                       if p.fmt.sharp {
-                               // Go syntax. Set the flag in the fmt and clear the sharp flag.
-                               p.fmt.sharp = false
-                               p.fmt.sharpV = true
-                       }
-                       if p.fmt.plus {
-                               // Struct-field syntax. Set the flag in the fmt and clear the plus flag.
-                               p.fmt.plus = false
-                               p.fmt.plusV = true
-                       }
-               }
+               verb, w := utf8.DecodeRuneInString(format[i:])
+               i += w
 
-               // Use space padding instead of zero padding to the right.
-               if p.fmt.minus {
-                       p.fmt.zero = false
+               switch {
+               case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
+                       p.buf.WriteByte('%')
+               case !p.goodArgNum:
+                       p.badArgNum(verb)
+               case argNum >= len(a): // No argument left over to print for the current verb.
+                       p.missingArg(verb)
+               case verb == 'v':
+                       // Go syntax
+                       p.fmt.sharpV = p.fmt.sharp
+                       p.fmt.sharp = false
+                       // Struct-field syntax
+                       p.fmt.plusV = p.fmt.plus
+                       p.fmt.plus = false
+                       fallthrough
+               default:
+                       p.printArg(a[argNum], verb)
+                       argNum++
                }
-
-               p.printArg(a[argNum], c)
-               argNum++
        }
 
        // Check for extra arguments unless the call accessed the arguments