From 4f13a9c5b1bfc9ec2213d9ee7d9df49661b119dd Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Tue, 1 Oct 2019 01:10:44 +0300 Subject: [PATCH] text/template/parse: use strings.Builder in String methods As mentioned in godoc, strings.Builder is more efficient for concatenating and building strings. Running a simple bench test on VariableNode.String() gives: benchmark old ns/op new ns/op delta BenchmarkParseLarge-8 25676831 24453285 -4.77% BenchmarkVariableString-8 296 115 -61.15% benchmark old allocs new allocs delta BenchmarkVariableString-8 8 3 -62.50% benchmark old bytes new bytes delta BenchmarkVariableString-8 112 72 -35.71% Change-Id: I13c9340080738fcad1edeed859d33ba608e4b05a Reviewed-on: https://go-review.googlesource.com/c/go/+/198078 Reviewed-by: Emmanuel Odeke Run-TryBot: Emmanuel Odeke TryBot-Result: Gobot Gobot --- src/text/template/parse/node.go | 41 ++++++++++++++------------- src/text/template/parse/parse_test.go | 16 +++++++++++ 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go index 2f921be2ec..2eb1af0a95 100644 --- a/src/text/template/parse/node.go +++ b/src/text/template/parse/node.go @@ -160,23 +160,23 @@ func (p *PipeNode) append(command *CommandNode) { } func (p *PipeNode) String() string { - s := "" + var sb strings.Builder if len(p.Decl) > 0 { for i, v := range p.Decl { if i > 0 { - s += ", " + sb.WriteString(", ") } - s += v.String() + sb.WriteString(v.String()) } - s += " := " + sb.WriteString(" := ") } for i, c := range p.Cmds { if i > 0 { - s += " | " + sb.WriteString(" | ") } - s += c.String() + sb.WriteString(c.String()) } - return s + return sb.String() } func (p *PipeNode) tree() *Tree { @@ -249,18 +249,20 @@ func (c *CommandNode) append(arg Node) { } func (c *CommandNode) String() string { - s := "" + var sb strings.Builder for i, arg := range c.Args { if i > 0 { - s += " " + sb.WriteByte(' ') } if arg, ok := arg.(*PipeNode); ok { - s += "(" + arg.String() + ")" + sb.WriteByte('(') + sb.WriteString(arg.String()) + sb.WriteByte(')') continue } - s += arg.String() + sb.WriteString(arg.String()) } - return s + return sb.String() } func (c *CommandNode) tree() *Tree { @@ -333,14 +335,14 @@ func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { } func (v *VariableNode) String() string { - s := "" + var sb strings.Builder for i, id := range v.Ident { if i > 0 { - s += "." + sb.WriteByte('.') } - s += id + sb.WriteString(id) } - return s + return sb.String() } func (v *VariableNode) tree() *Tree { @@ -426,11 +428,12 @@ func (t *Tree) newField(pos Pos, ident string) *FieldNode { } func (f *FieldNode) String() string { - s := "" + var sb strings.Builder for _, id := range f.Ident { - s += "." + id + sb.WriteByte('.') + sb.WriteString(id) } - return s + return sb.String() } func (f *FieldNode) tree() *Tree { diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go index 6932cf232e..371de5d67c 100644 --- a/src/text/template/parse/parse_test.go +++ b/src/text/template/parse/parse_test.go @@ -553,3 +553,19 @@ func BenchmarkParseLarge(b *testing.B) { } } } + +var sink string + +func BenchmarkVariableString(b *testing.B) { + v := &VariableNode{ + Ident: []string{"$", "A", "BB", "CCC", "THIS_IS_THE_VARIABLE_BEING_PROCESSED"}, + } + b.ResetTimer() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + sink = v.String() + } + if sink == "" { + b.Fatal("Benchmark was not run") + } +}