Commit 00da3a6e authored by Martin Möhrmann's avatar Martin Möhrmann Committed by Rob Pike

fmt: optimize byte slice and array formatting for %v and %d

Instead of calling printArg in fmtBytes to format each byte call
the byte formatting functions directly since it is known each
element is of type byte.

Add more tests for byte slice and array formatting.

name            old time/op  new time/op  delta
SprintfBytes-2   843ns ±16%   417ns ±11%  -50.58%  (p=0.000 n=20+20)

Change-Id: I5b907dbf52091e3de9710b09d67649c76f4c17e9
Reviewed-on: https://go-review.googlesource.com/20176
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 8c838192
......@@ -422,8 +422,57 @@ var fmtTests = []struct {
{"%v", &slice, "&[1 2 3 4 5]"},
{"%v", &islice, "&[1 hello 2.5 <nil>]"},
{"%v", &bslice, "&[1 2 3 4 5]"},
{"%v", []byte{1}, "[1]"},
// byte slices and arrays with %d and %v variants
{"%d", [0]byte{}, "[]"},
{"%d", [1]byte{123}, "[123]"},
{"%012d", []byte{}, "[]"},
{"%d", [3]byte{1, 11, 111}, "[1 11 111]"},
{"%d", [3]uint8{1, 11, 111}, "[1 11 111]"},
{"%06d", [3]byte{1, 11, 111}, "[000001 000011 000111]"},
{"%-6d", [3]byte{1, 11, 111}, "[1 11 111 ]"},
{"%-06d", [3]byte{1, 11, 111}, "[1 11 111 ]"}, // 0 has no effect when - is present.
{"%v", []byte{}, "[]"},
{"%012v", []byte{}, "[]"},
{"%#v", []byte{}, "[]byte{}"},
{"%#v", []uint8{}, "[]byte{}"},
{"%#012v", []byte{}, "[]byte{}"},
{"%v", []byte{123}, "[123]"},
{"%v", []byte{1, 11, 111}, "[1 11 111]"},
{"%6v", []byte{1, 11, 111}, "[ 1 11 111]"},
{"%06v", []byte{1, 11, 111}, "[000001 000011 000111]"},
{"%-6v", []byte{1, 11, 111}, "[1 11 111 ]"},
{"%-06v", []byte{1, 11, 111}, "[1 11 111 ]"},
{"%#v", []byte{1, 11, 111}, "[]byte{0x1, 0xb, 0x6f}"},
{"%#6v", []byte{1, 11, 111}, "[]byte{ 0x1, 0xb, 0x6f}"},
{"%#06v", []byte{1, 11, 111}, "[]byte{0x000001, 0x00000b, 0x00006f}"},
{"%#-6v", []byte{1, 11, 111}, "[]byte{0x1 , 0xb , 0x6f }"},
{"%#-06v", []byte{1, 11, 111}, "[]byte{0x1 , 0xb , 0x6f }"},
{"%v", [0]byte{}, "[]"},
{"%-12v", [0]byte{}, "[]"},
{"%#v", [0]byte{}, "[0]uint8{}"},
{"%#v", [0]uint8{}, "[0]uint8{}"},
{"%#-12v", [0]byte{}, "[0]uint8{}"},
{"%v", [1]byte{123}, "[123]"},
{"%v", [3]byte{1, 11, 111}, "[1 11 111]"},
{"%06v", [3]byte{1, 11, 111}, "[000001 000011 000111]"},
{"%-6v", [3]byte{1, 11, 111}, "[1 11 111 ]"},
{"%-06v", [3]byte{1, 11, 111}, "[1 11 111 ]"},
{"%#v", [3]byte{1, 11, 111}, "[3]uint8{0x1, 0xb, 0x6f}"},
{"%#6v", [3]byte{1, 11, 111}, "[3]uint8{ 0x1, 0xb, 0x6f}"},
{"%#06v", [3]byte{1, 11, 111}, "[3]uint8{0x000001, 0x00000b, 0x00006f}"},
{"%#-6v", [3]byte{1, 11, 111}, "[3]uint8{0x1 , 0xb , 0x6f }"},
{"%#-06v", [3]byte{1, 11, 111}, "[3]uint8{0x1 , 0xb , 0x6f }"},
// f.space should and f.plus should not have an effect with %v.
{"% v", []byte{1, 11, 111}, "[ 1 11 111]"},
{"%+v", [3]byte{1, 11, 111}, "[1 11 111]"},
{"%# -6v", []byte{1, 11, 111}, "[]byte{ 0x1 , 0xb , 0x6f }"},
{"%#+-6v", [3]byte{1, 11, 111}, "[3]uint8{0x1 , 0xb , 0x6f }"},
// f.space and f.plus should have an effect with %d.
{"% d", []byte{1, 11, 111}, "[ 1 11 111]"},
{"%+d", [3]byte{1, 11, 111}, "[+1 +11 +111]"},
{"%# -6d", []byte{1, 11, 111}, "[ 1 11 111 ]"},
{"%#+-6d", [3]byte{1, 11, 111}, "[+1 +11 +111 ]"},
// complexes with %v
{"%v", 1 + 2i, "(1+2i)"},
......@@ -809,6 +858,14 @@ var fmtTests = []struct {
// invalid reflect.Value doesn't crash.
{"%v", reflect.Value{}, "<invalid reflect.Value>"},
// Tests to check that not supported verbs generate an error string.
{"%☠", nil, "%!☠(<nil>)"},
{"%☠", interface{}(nil), "%!☠(<nil>)"},
{"%☠", []byte{0}, "%!☠([]uint8=[0])"},
{"%☠", []uint8{0}, "%!☠([]uint8=[0])"},
{"%☠", [1]byte{0}, "%!☠([1]uint8=[0])"},
{"%☠", [1]uint8{0}, "%!☠([1]uint8=[0])"},
}
// zeroFill generates zero-filled strings of the specified width. The length
......@@ -1041,6 +1098,15 @@ func BenchmarkSprintfHexBytes(b *testing.B) {
})
}
func BenchmarkSprintfBytes(b *testing.B) {
data := []byte("0123456789abcdef")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Sprintf("%v", data)
}
})
}
func BenchmarkManyArgs(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
var buf bytes.Buffer
......
......@@ -26,7 +26,7 @@ const (
badIndexString = "(BADINDEX)"
panicString = "(PANIC="
extraString = "%!(EXTRA "
bytesString = "[]byte{"
bytesString = "[]byte"
badWidthString = "%!(BADWIDTH)"
badPrecString = "%!(BADPREC)"
noVerbString = "%!(NOVERB)"
......@@ -522,45 +522,33 @@ func (p *pp) fmtString(v string, verb rune) {
}
}
func (p *pp) fmtBytes(v []byte, verb rune, typ reflect.Type, depth int) {
if verb == 'v' || verb == 'd' {
func (p *pp) fmtBytes(v []byte, verb rune, typeString string) {
switch verb {
case 'v', 'd':
if p.fmt.sharpV {
p.buf.WriteString(typeString)
if v == nil {
if typ == nil {
p.buf.WriteString("[]byte(nil)")
} else {
p.buf.WriteString(typ.String())
p.buf.WriteString(nilParenString)
}
p.buf.WriteString(nilParenString)
return
}
if typ == nil {
p.buf.WriteString(bytesString)
} else {
p.buf.WriteString(typ.String())
p.buf.WriteByte('{')
p.buf.WriteByte('{')
for i, c := range v {
if i > 0 {
p.buf.WriteString(commaSpaceString)
}
p.fmt0x64(uint64(c), true)
}
p.buf.WriteByte('}')
} else {
p.buf.WriteByte('[')
}
for i, c := range v {
if i > 0 {
if p.fmt.sharpV {
p.buf.WriteString(commaSpaceString)
} else {
for i, c := range v {
if i > 0 {
p.buf.WriteByte(' ')
}
p.fmt.integer(int64(c), 10, unsigned, ldigits)
}
p.printArg(c, 'v', depth+1)
}
if p.fmt.sharpV {
p.buf.WriteByte('}')
} else {
p.buf.WriteByte(']')
}
return
}
switch verb {
case 's':
p.fmt.fmt_s(string(v))
case 'x':
......@@ -788,7 +776,7 @@ func (p *pp) printArg(arg interface{}, verb rune, depth int) {
case string:
p.fmtString(f, verb)
case []byte:
p.fmtBytes(f, verb, nil, depth)
p.fmtBytes(f, verb, bytesString)
case reflect.Value:
p.printReflectValue(f, verb, depth)
return
......@@ -957,13 +945,13 @@ BigSwitch:
bytes[i] = byte(f.Index(i).Uint())
}
}
p.fmtBytes(bytes, verb, typ, depth)
p.fmtBytes(bytes, verb, typ.String())
break
}
if p.fmt.sharpV {
p.buf.WriteString(value.Type().String())
if f.Kind() == reflect.Slice && f.IsNil() {
p.buf.WriteString("(nil)")
p.buf.WriteString(nilParenString)
break
}
p.buf.WriteByte('{')
......
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