Skip to content

Commit 49da931

Browse files
martischrobpike
authored andcommitted
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 7162c4d commit 49da931

File tree

1 file changed

+58
-41
lines changed

1 file changed

+58
-41
lines changed

src/fmt/print.go

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -943,11 +943,24 @@ func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum
943943
return argNum, i + wid, ok
944944
}
945945

946+
func (p *pp) badArgNum(verb rune) {
947+
p.buf.WriteString(percentBangString)
948+
p.buf.WriteRune(verb)
949+
p.buf.WriteString(badIndexString)
950+
}
951+
952+
func (p *pp) missingArg(verb rune) {
953+
p.buf.WriteString(percentBangString)
954+
p.buf.WriteRune(verb)
955+
p.buf.WriteString(missingString)
956+
}
957+
946958
func (p *pp) doPrintf(format string, a []interface{}) {
947959
end := len(format)
948960
argNum := 0 // we process one argument per non-trivial format
949961
afterIndex := false // previous item in format was an index like [3].
950962
p.reordered = false
963+
formatLoop:
951964
for i := 0; i < end; {
952965
p.goodArgNum = true
953966
lasti := i
@@ -967,21 +980,40 @@ func (p *pp) doPrintf(format string, a []interface{}) {
967980

968981
// Do we have flags?
969982
p.fmt.clearflags()
970-
F:
983+
simpleFormat:
971984
for ; i < end; i++ {
972-
switch format[i] {
985+
c := format[i]
986+
switch c {
973987
case '#':
974988
p.fmt.sharp = true
975989
case '0':
976-
p.fmt.zero = true
990+
p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left.
977991
case '+':
978992
p.fmt.plus = true
979993
case '-':
980994
p.fmt.minus = true
995+
p.fmt.zero = false // Do not pad with zeros to the right.
981996
case ' ':
982997
p.fmt.space = true
983998
default:
984-
break F
999+
// Fast path for common case of ascii lower case simple verbs
1000+
// without precision or width or argument indices.
1001+
if 'a' <= c && c <= 'z' && argNum < len(a) {
1002+
if c == 'v' {
1003+
// Go syntax
1004+
p.fmt.sharpV = p.fmt.sharp
1005+
p.fmt.sharp = false
1006+
// Struct-field syntax
1007+
p.fmt.plusV = p.fmt.plus
1008+
p.fmt.plus = false
1009+
}
1010+
p.printArg(a[argNum], rune(c))
1011+
argNum++
1012+
i++
1013+
continue formatLoop
1014+
}
1015+
// Format is more complex than simple flags and a verb or is malformed.
1016+
break simpleFormat
9851017
}
9861018
}
9871019

@@ -1002,6 +1034,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
10021034
if p.fmt.wid < 0 {
10031035
p.fmt.wid = -p.fmt.wid
10041036
p.fmt.minus = true
1037+
p.fmt.zero = false // Do not pad with zeros to the right.
10051038
}
10061039
afterIndex = false
10071040
} else {
@@ -1045,47 +1078,31 @@ func (p *pp) doPrintf(format string, a []interface{}) {
10451078

10461079
if i >= end {
10471080
p.buf.WriteString(noVerbString)
1048-
continue
1049-
}
1050-
c, w := utf8.DecodeRuneInString(format[i:])
1051-
i += w
1052-
// percent is special - absorbs no operand
1053-
if c == '%' {
1054-
p.buf.WriteByte('%') // We ignore width and prec.
1055-
continue
1056-
}
1057-
if !p.goodArgNum {
1058-
p.buf.WriteString(percentBangString)
1059-
p.buf.WriteRune(c)
1060-
p.buf.WriteString(badIndexString)
1061-
continue
1062-
} else if argNum >= len(a) { // out of operands
1063-
p.buf.WriteString(percentBangString)
1064-
p.buf.WriteRune(c)
1065-
p.buf.WriteString(missingString)
1066-
continue
1081+
break
10671082
}
10681083

1069-
if c == 'v' {
1070-
if p.fmt.sharp {
1071-
// Go syntax. Set the flag in the fmt and clear the sharp flag.
1072-
p.fmt.sharp = false
1073-
p.fmt.sharpV = true
1074-
}
1075-
if p.fmt.plus {
1076-
// Struct-field syntax. Set the flag in the fmt and clear the plus flag.
1077-
p.fmt.plus = false
1078-
p.fmt.plusV = true
1079-
}
1080-
}
1084+
verb, w := utf8.DecodeRuneInString(format[i:])
1085+
i += w
10811086

1082-
// Use space padding instead of zero padding to the right.
1083-
if p.fmt.minus {
1084-
p.fmt.zero = false
1087+
switch {
1088+
case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec.
1089+
p.buf.WriteByte('%')
1090+
case !p.goodArgNum:
1091+
p.badArgNum(verb)
1092+
case argNum >= len(a): // No argument left over to print for the current verb.
1093+
p.missingArg(verb)
1094+
case verb == 'v':
1095+
// Go syntax
1096+
p.fmt.sharpV = p.fmt.sharp
1097+
p.fmt.sharp = false
1098+
// Struct-field syntax
1099+
p.fmt.plusV = p.fmt.plus
1100+
p.fmt.plus = false
1101+
fallthrough
1102+
default:
1103+
p.printArg(a[argNum], verb)
1104+
argNum++
10851105
}
1086-
1087-
p.printArg(a[argNum], c)
1088-
argNum++
10891106
}
10901107

10911108
// Check for extra arguments unless the call accessed the arguments

0 commit comments

Comments
 (0)