1
0
mirror of https://github.com/golang/go synced 2024-11-24 13:50:13 -07:00

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: Rob Pike <r@golang.org>
This commit is contained in:
Martin Möhrmann 2016-03-03 17:06:43 +01:00 committed by Rob Pike
parent 8c838192b8
commit 00da3a6ec3
2 changed files with 87 additions and 33 deletions

View File

@ -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

View File

@ -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)
}
return
}
if typ == nil {
p.buf.WriteString(bytesString)
} else {
p.buf.WriteString(typ.String())
p.buf.WriteByte('{')
}
} else {
p.buf.WriteByte('[')
}
for i, c := range v {
if i > 0 {
if p.fmt.sharpV {
p.buf.WriteString(commaSpaceString)
} else {
p.buf.WriteByte(' ')
}
p.fmt0x64(uint64(c), true)
}
p.printArg(c, 'v', depth+1)
}
if p.fmt.sharpV {
p.buf.WriteByte('}')
} else {
p.buf.WriteByte('[')
for i, c := range v {
if i > 0 {
p.buf.WriteByte(' ')
}
p.fmt.integer(int64(c), 10, unsigned, ldigits)
}
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('{')