Commit 49da9312 authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Rob Pike

fmt: cleanup and optimize doPrintf for simple formats

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: default avatarRob Pike <r@golang.org>
parent 7162c4d0
...@@ -943,11 +943,24 @@ func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum ...@@ -943,11 +943,24 @@ func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum
return argNum, i + wid, ok 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{}) { func (p *pp) doPrintf(format string, a []interface{}) {
end := len(format) end := len(format)
argNum := 0 // we process one argument per non-trivial format argNum := 0 // we process one argument per non-trivial format
afterIndex := false // previous item in format was an index like [3]. afterIndex := false // previous item in format was an index like [3].
p.reordered = false p.reordered = false
formatLoop:
for i := 0; i < end; { for i := 0; i < end; {
p.goodArgNum = true p.goodArgNum = true
lasti := i lasti := i
...@@ -967,21 +980,40 @@ func (p *pp) doPrintf(format string, a []interface{}) { ...@@ -967,21 +980,40 @@ func (p *pp) doPrintf(format string, a []interface{}) {
// Do we have flags? // Do we have flags?
p.fmt.clearflags() p.fmt.clearflags()
F: simpleFormat:
for ; i < end; i++ { for ; i < end; i++ {
switch format[i] { c := format[i]
switch c {
case '#': case '#':
p.fmt.sharp = true p.fmt.sharp = true
case '0': case '0':
p.fmt.zero = true p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left.
case '+': case '+':
p.fmt.plus = true p.fmt.plus = true
case '-': case '-':
p.fmt.minus = true p.fmt.minus = true
p.fmt.zero = false // Do not pad with zeros to the right.
case ' ': case ' ':
p.fmt.space = true p.fmt.space = true
default: 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{}) { ...@@ -1002,6 +1034,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
if p.fmt.wid < 0 { if p.fmt.wid < 0 {
p.fmt.wid = -p.fmt.wid p.fmt.wid = -p.fmt.wid
p.fmt.minus = true p.fmt.minus = true
p.fmt.zero = false // Do not pad with zeros to the right.
} }
afterIndex = false afterIndex = false
} else { } else {
...@@ -1045,47 +1078,31 @@ func (p *pp) doPrintf(format string, a []interface{}) { ...@@ -1045,47 +1078,31 @@ func (p *pp) doPrintf(format string, a []interface{}) {
if i >= end { if i >= end {
p.buf.WriteString(noVerbString) p.buf.WriteString(noVerbString)
continue break
}
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
} }
if c == 'v' { verb, w := utf8.DecodeRuneInString(format[i:])
if p.fmt.sharp { i += w
// 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
}
}
// Use space padding instead of zero padding to the right. switch {
if p.fmt.minus { case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
p.fmt.zero = false 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 // Check for extra arguments unless the call accessed the arguments
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment