this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cue/parser: reuse commentState allocations

parser.openComments is called for practically every node being parsed,
even if there are no comments to be parsed in the input.
Given that this function allocates, this leads to GC overhead.
Reuse the allocations of commentState by keeping a stack of free ones.
Note that we also tweak the code to allow reusing its groups slice.

As measured by CUE_BENCH and benchstat, formatting the 412k lines of CUE
in https://github.com/uhthomas/automata is about 10% faster,
thanks to allocating 30% less memory:

│ old │ new │
│ sec/op │ sec/op vs base │
FmtAutomata 370.5m ± 7% 332.2m ± 14% ~ (p=0.083 n=8)

│ old │ new │
│ B/op │ B/op vs base │
FmtAutomata 292.5Mi ± 0% 206.6Mi ± 0% -29.36% (p=0.000 n=8)

│ old │ new │
│ allocs/op │ allocs/op vs base │
FmtAutomata 3.363M ± 0% 2.237M ± 0% -33.49% (p=0.000 n=8)

Similarly, a 900k line CUE data file with no comments from
https://github.com/roman-mazur/cuetf sees a much more significant
speed-up, likely due to the large amount of CUE fields being parsed:

│ old │ new │
│ sec/op │ sec/op vs base │
FmtAwsSchema 3.059 ± 2% 2.364 ± 1% -22.73% (p=0.000 n=8)

│ old │ new │
│ B/op │ B/op vs base │
FmtAwsSchema 2.355Gi ± 0% 1.547Gi ± 0% -34.30% (p=0.000 n=8)

│ old │ new │
│ allocs/op │ allocs/op vs base │
FmtAwsSchema 27.13M ± 0% 16.29M ± 0% -39.95% (p=0.000 n=8)

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I91089f2b5cffa6dc5fc84151b6cbbeb3cd5eeceb
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1225189
Reviewed-by: Matthew Sackman <matthew@cue.works>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>

+29 -15
+29 -15
cue/parser/parser.go
··· 45 45 indent int // indentation used for tracing output 46 46 47 47 // Comments 48 - leadComment *ast.CommentGroup 49 - comments *commentState 48 + leadComment *ast.CommentGroup 49 + comments *commentState 50 + commentStack []*commentState // to reuse [commentState] allocations 50 51 51 52 // Next token, filled by [parser.next0]. 52 53 pos token.Pos // token position ··· 108 109 lastPos int8 109 110 } 110 111 112 + func (p *parser) allocCommentState() *commentState { 113 + if n := len(p.commentStack); n > 0 { 114 + c := p.commentStack[n-1] 115 + p.commentStack = p.commentStack[:n-1] 116 + return c 117 + } 118 + return &commentState{} 119 + } 120 + 121 + func (p *parser) freeCommentState(c *commentState) { 122 + // Ensure no pointers remain, which can hold onto memory. 123 + // We only reuse the groups slice capacity. 124 + *c = commentState{groups: c.groups[:0]} 125 + p.commentStack = append(p.commentStack, c) 126 + } 127 + 111 128 // openComments reserves the next doc comment for the caller and flushes 112 129 func (p *parser) openComments() *commentState { 113 - child := &commentState{ 114 - parent: p.comments, 115 - } 130 + child := p.allocCommentState() 131 + child.parent = p.comments 116 132 if c := p.comments; c != nil && c.isList > 0 { 117 133 if c.lastChild != nil { 118 134 var groups []*ast.CommentGroup ··· 129 145 } 130 146 } 131 147 ast.SetComments(c.lastChild, groups) 132 - c.groups = nil 133 148 } else { 134 - c.lastChild = nil 135 149 // attach before next 136 150 for _, cg := range c.groups { 137 151 cg.Position = 0 138 152 } 139 - child.groups = c.groups 140 - c.groups = nil 153 + child.groups = append(child.groups, c.groups...) 141 154 } 155 + c.groups = c.groups[:0] 142 156 } 143 157 if p.leadComment != nil { 144 158 child.groups = append(child.groups, p.leadComment) ··· 155 169 p.comments.isList++ 156 170 return 157 171 } 158 - c := &commentState{ 159 - parent: p.comments, 160 - isList: 1, 161 - } 172 + c := p.allocCommentState() 173 + c.parent = p.comments 174 + c.isList = 1 162 175 p.comments = c 163 176 } 164 177 ··· 174 187 cg.Position = c.lastPos 175 188 ast.AddComment(c.lastChild, cg) 176 189 } 177 - c.groups = nil 190 + c.groups = c.groups[:0] 178 191 } 179 192 switch c.isList--; { 180 193 case c.isList < 0: ··· 191 204 } 192 205 parent.pos++ 193 206 p.comments = parent 207 + p.freeCommentState(c) 194 208 } 195 209 } 196 210 ··· 217 231 } 218 232 } 219 233 } 220 - c.groups = nil 234 + p.freeCommentState(c) 221 235 return n 222 236 } 223 237