From: Martin Möhrmann Date: Sun, 20 Mar 2016 14:24:50 +0000 (+0100) Subject: fmt: cleanup and optimize doPrintf for simple formats X-Git-Tag: go1.7beta1~1145 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=49da931268fe4d1fcee988c05b1d65dad8668506;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Rob Pike --- diff --git a/src/fmt/print.go b/src/fmt/print.go index 0064ab3734..06d3049b8f 100644 --- a/src/fmt/print.go +++ b/src/fmt/print.go @@ -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