Commit debbb1e7 authored by Ariel Mashraki's avatar Ariel Mashraki Committed by Brad Fitzpatrick

text/template/parse: speed up nodes printing

This CL is a follow up for 198080.
Added a private writeTo method to the Node interface,
in order to use the same builder for printing all nodes
in the tree. Benchmark output against master:

benchmark                     old ns/op     new ns/op     delta
BenchmarkParseLarge-8         24594994      25292054      +2.83%
BenchmarkVariableString-8     117           118           +0.85%
BenchmarkListString-8         10475         3353          -67.99%

benchmark                     old allocs     new allocs     delta
BenchmarkVariableString-8     3              3              +0.00%
BenchmarkListString-8         149            31             -79.19%

benchmark                     old bytes     new bytes     delta
BenchmarkVariableString-8     72            72            +0.00%
BenchmarkListString-8         5698          1608          -71.78%

Change-Id: I2b1cf07cda65c1b80083fb99671289423700feba
Reviewed-on: https://go-review.googlesource.com/c/go/+/198278Reviewed-by: default avatarRob Pike <r@golang.org>
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent f0e940eb
...@@ -28,6 +28,8 @@ type Node interface { ...@@ -28,6 +28,8 @@ type Node interface {
// tree returns the containing *Tree. // tree returns the containing *Tree.
// It is unexported so all implementations of Node are in this package. // It is unexported so all implementations of Node are in this package.
tree() *Tree tree() *Tree
// writeTo writes the String output to the builder.
writeTo(*strings.Builder)
} }
// NodeType identifies the type of a parse tree node. // NodeType identifies the type of a parse tree node.
...@@ -94,10 +96,14 @@ func (l *ListNode) tree() *Tree { ...@@ -94,10 +96,14 @@ func (l *ListNode) tree() *Tree {
func (l *ListNode) String() string { func (l *ListNode) String() string {
var sb strings.Builder var sb strings.Builder
l.writeTo(&sb)
return sb.String()
}
func (l *ListNode) writeTo(sb *strings.Builder) {
for _, n := range l.Nodes { for _, n := range l.Nodes {
sb.WriteString(n.String()) n.writeTo(sb)
} }
return sb.String()
} }
func (l *ListNode) CopyList() *ListNode { func (l *ListNode) CopyList() *ListNode {
...@@ -131,6 +137,10 @@ func (t *TextNode) String() string { ...@@ -131,6 +137,10 @@ func (t *TextNode) String() string {
return fmt.Sprintf(textFormat, t.Text) return fmt.Sprintf(textFormat, t.Text)
} }
func (t *TextNode) writeTo(sb *strings.Builder) {
sb.WriteString(t.String())
}
func (t *TextNode) tree() *Tree { func (t *TextNode) tree() *Tree {
return t.tr return t.tr
} }
...@@ -160,12 +170,17 @@ func (p *PipeNode) append(command *CommandNode) { ...@@ -160,12 +170,17 @@ func (p *PipeNode) append(command *CommandNode) {
func (p *PipeNode) String() string { func (p *PipeNode) String() string {
var sb strings.Builder var sb strings.Builder
p.writeTo(&sb)
return sb.String()
}
func (p *PipeNode) writeTo(sb *strings.Builder) {
if len(p.Decl) > 0 { if len(p.Decl) > 0 {
for i, v := range p.Decl { for i, v := range p.Decl {
if i > 0 { if i > 0 {
sb.WriteString(", ") sb.WriteString(", ")
} }
sb.WriteString(v.String()) v.writeTo(sb)
} }
sb.WriteString(" := ") sb.WriteString(" := ")
} }
...@@ -173,9 +188,8 @@ func (p *PipeNode) String() string { ...@@ -173,9 +188,8 @@ func (p *PipeNode) String() string {
if i > 0 { if i > 0 {
sb.WriteString(" | ") sb.WriteString(" | ")
} }
sb.WriteString(c.String()) c.writeTo(sb)
} }
return sb.String()
} }
func (p *PipeNode) tree() *Tree { func (p *PipeNode) tree() *Tree {
...@@ -218,8 +232,15 @@ func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { ...@@ -218,8 +232,15 @@ func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode {
} }
func (a *ActionNode) String() string { func (a *ActionNode) String() string {
return fmt.Sprintf("{{%s}}", a.Pipe) var sb strings.Builder
a.writeTo(&sb)
return sb.String()
}
func (a *ActionNode) writeTo(sb *strings.Builder) {
sb.WriteString("{{")
a.Pipe.writeTo(sb)
sb.WriteString("}}")
} }
func (a *ActionNode) tree() *Tree { func (a *ActionNode) tree() *Tree {
...@@ -249,19 +270,23 @@ func (c *CommandNode) append(arg Node) { ...@@ -249,19 +270,23 @@ func (c *CommandNode) append(arg Node) {
func (c *CommandNode) String() string { func (c *CommandNode) String() string {
var sb strings.Builder var sb strings.Builder
c.writeTo(&sb)
return sb.String()
}
func (c *CommandNode) writeTo(sb *strings.Builder) {
for i, arg := range c.Args { for i, arg := range c.Args {
if i > 0 { if i > 0 {
sb.WriteByte(' ') sb.WriteByte(' ')
} }
if arg, ok := arg.(*PipeNode); ok { if arg, ok := arg.(*PipeNode); ok {
sb.WriteByte('(') sb.WriteByte('(')
sb.WriteString(arg.String()) arg.writeTo(sb)
sb.WriteByte(')') sb.WriteByte(')')
continue continue
} }
sb.WriteString(arg.String()) arg.writeTo(sb)
} }
return sb.String()
} }
func (c *CommandNode) tree() *Tree { func (c *CommandNode) tree() *Tree {
...@@ -312,6 +337,10 @@ func (i *IdentifierNode) String() string { ...@@ -312,6 +337,10 @@ func (i *IdentifierNode) String() string {
return i.Ident return i.Ident
} }
func (i *IdentifierNode) writeTo(sb *strings.Builder) {
sb.WriteString(i.String())
}
func (i *IdentifierNode) tree() *Tree { func (i *IdentifierNode) tree() *Tree {
return i.tr return i.tr
} }
...@@ -335,13 +364,17 @@ func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { ...@@ -335,13 +364,17 @@ func (t *Tree) newVariable(pos Pos, ident string) *VariableNode {
func (v *VariableNode) String() string { func (v *VariableNode) String() string {
var sb strings.Builder var sb strings.Builder
v.writeTo(&sb)
return sb.String()
}
func (v *VariableNode) writeTo(sb *strings.Builder) {
for i, id := range v.Ident { for i, id := range v.Ident {
if i > 0 { if i > 0 {
sb.WriteByte('.') sb.WriteByte('.')
} }
sb.WriteString(id) sb.WriteString(id)
} }
return sb.String()
} }
func (v *VariableNode) tree() *Tree { func (v *VariableNode) tree() *Tree {
...@@ -374,6 +407,10 @@ func (d *DotNode) String() string { ...@@ -374,6 +407,10 @@ func (d *DotNode) String() string {
return "." return "."
} }
func (d *DotNode) writeTo(sb *strings.Builder) {
sb.WriteString(d.String())
}
func (d *DotNode) tree() *Tree { func (d *DotNode) tree() *Tree {
return d.tr return d.tr
} }
...@@ -404,6 +441,10 @@ func (n *NilNode) String() string { ...@@ -404,6 +441,10 @@ func (n *NilNode) String() string {
return "nil" return "nil"
} }
func (n *NilNode) writeTo(sb *strings.Builder) {
sb.WriteString(n.String())
}
func (n *NilNode) tree() *Tree { func (n *NilNode) tree() *Tree {
return n.tr return n.tr
} }
...@@ -428,11 +469,15 @@ func (t *Tree) newField(pos Pos, ident string) *FieldNode { ...@@ -428,11 +469,15 @@ func (t *Tree) newField(pos Pos, ident string) *FieldNode {
func (f *FieldNode) String() string { func (f *FieldNode) String() string {
var sb strings.Builder var sb strings.Builder
f.writeTo(&sb)
return sb.String()
}
func (f *FieldNode) writeTo(sb *strings.Builder) {
for _, id := range f.Ident { for _, id := range f.Ident {
sb.WriteByte('.') sb.WriteByte('.')
sb.WriteString(id) sb.WriteString(id)
} }
return sb.String()
} }
func (f *FieldNode) tree() *Tree { func (f *FieldNode) tree() *Tree {
...@@ -472,18 +517,22 @@ func (c *ChainNode) Add(field string) { ...@@ -472,18 +517,22 @@ func (c *ChainNode) Add(field string) {
func (c *ChainNode) String() string { func (c *ChainNode) String() string {
var sb strings.Builder var sb strings.Builder
c.writeTo(&sb)
return sb.String()
}
func (c *ChainNode) writeTo(sb *strings.Builder) {
if _, ok := c.Node.(*PipeNode); ok { if _, ok := c.Node.(*PipeNode); ok {
sb.WriteByte('(') sb.WriteByte('(')
sb.WriteString(c.Node.String()) c.Node.writeTo(sb)
sb.WriteByte(')') sb.WriteByte(')')
} else { } else {
sb.WriteString(c.Node.String()) c.Node.writeTo(sb)
} }
for _, field := range c.Field { for _, field := range c.Field {
sb.WriteByte('.') sb.WriteByte('.')
sb.WriteString(field) sb.WriteString(field)
} }
return sb.String()
} }
func (c *ChainNode) tree() *Tree { func (c *ChainNode) tree() *Tree {
...@@ -513,6 +562,10 @@ func (b *BoolNode) String() string { ...@@ -513,6 +562,10 @@ func (b *BoolNode) String() string {
return "false" return "false"
} }
func (b *BoolNode) writeTo(sb *strings.Builder) {
sb.WriteString(b.String())
}
func (b *BoolNode) tree() *Tree { func (b *BoolNode) tree() *Tree {
return b.tr return b.tr
} }
...@@ -646,6 +699,10 @@ func (n *NumberNode) String() string { ...@@ -646,6 +699,10 @@ func (n *NumberNode) String() string {
return n.Text return n.Text
} }
func (n *NumberNode) writeTo(sb *strings.Builder) {
sb.WriteString(n.String())
}
func (n *NumberNode) tree() *Tree { func (n *NumberNode) tree() *Tree {
return n.tr return n.tr
} }
...@@ -673,6 +730,10 @@ func (s *StringNode) String() string { ...@@ -673,6 +730,10 @@ func (s *StringNode) String() string {
return s.Quoted return s.Quoted
} }
func (s *StringNode) writeTo(sb *strings.Builder) {
sb.WriteString(s.String())
}
func (s *StringNode) tree() *Tree { func (s *StringNode) tree() *Tree {
return s.tr return s.tr
} }
...@@ -697,6 +758,10 @@ func (e *endNode) String() string { ...@@ -697,6 +758,10 @@ func (e *endNode) String() string {
return "{{end}}" return "{{end}}"
} }
func (e *endNode) writeTo(sb *strings.Builder) {
sb.WriteString(e.String())
}
func (e *endNode) tree() *Tree { func (e *endNode) tree() *Tree {
return e.tr return e.tr
} }
...@@ -725,6 +790,10 @@ func (e *elseNode) String() string { ...@@ -725,6 +790,10 @@ func (e *elseNode) String() string {
return "{{else}}" return "{{else}}"
} }
func (e *elseNode) writeTo(sb *strings.Builder) {
sb.WriteString(e.String())
}
func (e *elseNode) tree() *Tree { func (e *elseNode) tree() *Tree {
return e.tr return e.tr
} }
...@@ -745,6 +814,12 @@ type BranchNode struct { ...@@ -745,6 +814,12 @@ type BranchNode struct {
} }
func (b *BranchNode) String() string { func (b *BranchNode) String() string {
var sb strings.Builder
b.writeTo(&sb)
return sb.String()
}
func (b *BranchNode) writeTo(sb *strings.Builder) {
name := "" name := ""
switch b.NodeType { switch b.NodeType {
case NodeIf: case NodeIf:
...@@ -756,10 +831,17 @@ func (b *BranchNode) String() string { ...@@ -756,10 +831,17 @@ func (b *BranchNode) String() string {
default: default:
panic("unknown branch type") panic("unknown branch type")
} }
sb.WriteString("{{")
sb.WriteString(name)
sb.WriteByte(' ')
b.Pipe.writeTo(sb)
sb.WriteString("}}")
b.List.writeTo(sb)
if b.ElseList != nil { if b.ElseList != nil {
return fmt.Sprintf("{{%s %s}}%s{{else}}%s{{end}}", name, b.Pipe, b.List, b.ElseList) sb.WriteString("{{else}}")
b.ElseList.writeTo(sb)
} }
return fmt.Sprintf("{{%s %s}}%s{{end}}", name, b.Pipe, b.List) sb.WriteString("{{end}}")
} }
func (b *BranchNode) tree() *Tree { func (b *BranchNode) tree() *Tree {
...@@ -833,10 +915,19 @@ func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *Temp ...@@ -833,10 +915,19 @@ func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *Temp
} }
func (t *TemplateNode) String() string { func (t *TemplateNode) String() string {
if t.Pipe == nil { var sb strings.Builder
return fmt.Sprintf("{{template %q}}", t.Name) t.writeTo(&sb)
return sb.String()
}
func (t *TemplateNode) writeTo(sb *strings.Builder) {
sb.WriteString("{{template ")
sb.WriteString(strconv.Quote(t.Name))
if t.Pipe != nil {
sb.WriteByte(' ')
t.Pipe.writeTo(sb)
} }
return fmt.Sprintf("{{template %q %s}}", t.Name, t.Pipe) sb.WriteString("}}")
} }
func (t *TemplateNode) tree() *Tree { func (t *TemplateNode) tree() *Tree {
......
...@@ -304,7 +304,8 @@ var parseTests = []parseTest{ ...@@ -304,7 +304,8 @@ var parseTests = []parseTest{
} }
var builtins = map[string]interface{}{ var builtins = map[string]interface{}{
"printf": fmt.Sprintf, "printf": fmt.Sprintf,
"contains": strings.Contains,
} }
func testParse(doCopy bool, t *testing.T) { func testParse(doCopy bool, t *testing.T) {
...@@ -571,7 +572,24 @@ func BenchmarkVariableString(b *testing.B) { ...@@ -571,7 +572,24 @@ func BenchmarkVariableString(b *testing.B) {
} }
func BenchmarkListString(b *testing.B) { func BenchmarkListString(b *testing.B) {
text := `{{ (printf .Field1.Field2.Field3).Value }}` text := `
{{(printf .Field1.Field2.Field3).Value}}
{{$x := (printf .Field1.Field2.Field3).Value}}
{{$y := (printf $x.Field1.Field2.Field3).Value}}
{{$z := $y.Field1.Field2.Field3}}
{{if contains $y $z}}
{{printf "%q" $y}}
{{else}}
{{printf "%q" $x}}
{{end}}
{{with $z.Field1 | contains "boring"}}
{{printf "%q" . | printf "%s"}}
{{else}}
{{printf "%d %d %d" 11 11 11}}
{{printf "%d %d %s" 22 22 $x.Field1.Field2.Field3 | printf "%s"}}
{{printf "%v" (contains $z.Field1.Field2 $y)}}
{{end}}
`
tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins) tree, err := New("bench").Parse(text, "", "", make(map[string]*Tree), builtins)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
......
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