From 49da931268fe4d1fcee988c05b1d65dad8668506 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20Mo=CC=88hrmann?= Date: Sun, 20 Mar 2016 15:24:50 +0100 Subject: [PATCH] fmt: cleanup and optimize doPrintf for simple formats MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- src/fmt/print.go | 99 ++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 41 deletions(-) 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 -- 2.48.1