Skip to content

Commit e78263d

Browse files
authored
optimize node slices (#33)
Use a single struct type `NodeSlice` to wrap all kinds of `ast.Node` slices; this allows us to have an allocation pool slice that can be used to avoid allocations in most cases. While some operations like `NodeSlice.At` become slower, other operations like `NodeSlice.SliceInto` are far more efficient: we can re-use memory even in re-slicing cases. Benchmark results: name old time/op new time/op delta Match/failFast-8 178ns ± 1% 182ns ± 1% +2.18% (p=0.000 n=10+10) Match/failCall-8 427ns ± 1% 414ns ± 1% -3.00% (p=0.000 n=10+9) Match/failCallFast-8 176ns ± 0% 166ns ± 2% -6.07% (p=0.000 n=8+10) Match/assign-8 251ns ± 1% 245ns ± 0% -2.29% (p=0.000 n=10+7) Match/assignMulti-8 874ns ± 2% 586ns ± 1% -32.96% (p=0.000 n=10+10) Match/simpleLit-8 109ns ± 1% 104ns ± 4% -4.15% (p=0.000 n=10+10) Match/simpleBinaryOp-8 234ns ± 1% 235ns ± 4% ~ (p=0.986 n=10+10) Match/simpleSelectorExpr-8 326ns ± 1% 308ns ± 1% -5.55% (p=0.000 n=9+10) Match/simpleCall-8 310ns ± 1% 296ns ± 1% -4.25% (p=0.000 n=10+10) Match/selectorExpr-8 343ns ± 1% 339ns ± 1% -1.10% (p=0.004 n=10+9) Match/sliceExpr-8 261ns ± 1% 243ns ± 0% -6.89% (p=0.000 n=10+9) Match/any-8 181ns ± 1% 187ns ± 3% +3.04% (p=0.000 n=10+10) Match/anyCall-8 693ns ± 1% 514ns ± 1% -25.87% (p=0.000 n=10+10) Match/ifStmt-8 558ns ± 1% 399ns ± 1% -28.51% (p=0.000 n=10+10) Match/optStmt1-8 399ns ± 1% 375ns ± 1% -6.13% (p=0.000 n=10+10) Match/optStmt2-8 276ns ± 1% 247ns ± 1% -10.64% (p=0.000 n=10+9) Match/namedOptStmt1-8 2.00µs ± 1% 1.61µs ± 1% -19.18% (p=0.000 n=9+10) Match/namedOptStmt2-8 1.33µs ± 1% 1.06µs ± 1% -20.07% (p=0.000 n=10+10) Match/branchStmt-8 139ns ± 1% 137ns ± 1% -1.33% (p=0.000 n=10+10) Match/multiStmt-8 1.23µs ± 1% 0.98µs ± 1% -20.06% (p=0.000 n=9+10) Match/multiExpr-8 1.33µs ± 1% 1.03µs ± 1% -22.31% (p=0.000 n=9+10) Match/variadicCall-8 254ns ± 2% 252ns ± 0% -0.79% (p=0.003 n=10+7) Match/capture1-8 170ns ± 0% 167ns ± 1% -2.24% (p=0.000 n=9+9) Match/capture2-8 257ns ± 1% 250ns ± 1% -2.75% (p=0.000 n=10+10) Match/capture8-8 928ns ± 1% 919ns ± 1% -0.95% (p=0.000 n=10+10) Match/capture2same-8 296ns ± 1% 283ns ± 0% -4.43% (p=0.000 n=10+9) Match/capture8same-8 1.06µs ± 1% 1.03µs ± 1% -3.41% (p=0.000 n=9+10) Match/captureBacktrackLeft-8 1.65µs ± 1% 1.10µs ± 1% -32.96% (p=0.000 n=10+8) Match/captureBacktrackRight-8 1.08µs ± 2% 0.81µs ± 1% -24.89% (p=0.000 n=10+10) Match/exprList-8 2.62µs ± 1% 2.32µs ± 1% -11.61% (p=0.000 n=10+8) [Geo mean] 453ns 405ns -10.66% name old alloc/op new alloc/op delta Match/failFast-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/failCall-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/failCallFast-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/assign-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/assignMulti-8 192B ± 0% 96B ± 0% -50.00% (p=0.000 n=10+10) Match/simpleLit-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/simpleBinaryOp-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/simpleSelectorExpr-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/simpleCall-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/selectorExpr-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/sliceExpr-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/any-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/anyCall-8 144B ± 0% 96B ± 0% -33.33% (p=0.000 n=10+10) Match/ifStmt-8 80.0B ± 0% 32.0B ± 0% -60.00% (p=0.000 n=10+10) Match/optStmt1-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/optStmt2-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/namedOptStmt1-8 344B ± 0% 224B ± 0% -34.88% (p=0.000 n=10+10) Match/namedOptStmt2-8 168B ± 0% 96B ± 0% -42.86% (p=0.000 n=10+10) Match/branchStmt-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/multiStmt-8 256B ± 0% 160B ± 0% -37.50% (p=0.000 n=10+10) Match/multiExpr-8 256B ± 0% 160B ± 0% -37.50% (p=0.000 n=10+10) Match/variadicCall-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/capture1-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/capture2-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/capture8-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/capture2same-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/capture8same-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) Match/captureBacktrackLeft-8 288B ± 0% 96B ± 0% -66.67% (p=0.000 n=10+10) Match/captureBacktrackRight-8 168B ± 0% 96B ± 0% -42.86% (p=0.000 n=10+10) Match/exprList-8 256B ± 0% 160B ± 0% -37.50% (p=0.000 n=10+10) [Geo mean] 59.0B 48.2B -18.32%
1 parent dce9607 commit e78263d

File tree

7 files changed

+357
-145
lines changed

7 files changed

+357
-145
lines changed

compile.go

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,19 @@ func (c *compiler) compileNode(n ast.Node) {
122122
c.compileStmt(n)
123123
case *ast.ValueSpec:
124124
c.compileValueSpec(n)
125-
case stmtSlice:
126-
c.compileStmtSlice(n)
127-
case declSlice:
128-
c.compileDeclSlice(n)
129-
case ExprSlice:
130-
c.compileExprSlice(n)
131125
case *rangeClause:
132126
c.compileRangeClause(n)
133127
case *rangeHeader:
134128
c.compileRangeHeader(n)
129+
case *NodeSlice:
130+
switch n.Kind {
131+
case StmtNodeSlice:
132+
c.compileStmtSlice(n.stmtSlice)
133+
case DeclNodeSlice:
134+
c.compileDeclSlice(n.declSlice)
135+
case ExprNodeSlice:
136+
c.compileExprSlice(n.exprSlice)
137+
}
135138
default:
136139
panic(c.errorf(n, "compileNode: unexpected %T", n))
137140
}
@@ -1191,15 +1194,15 @@ func (c *compiler) compileSendStmt(n *ast.SendStmt) {
11911194
c.compileExpr(n.Value)
11921195
}
11931196

1194-
func (c *compiler) compileDeclSlice(decls declSlice) {
1197+
func (c *compiler) compileDeclSlice(decls []ast.Decl) {
11951198
c.emitInstOp(opMultiDecl)
11961199
for _, n := range decls {
11971200
c.compileDecl(n)
11981201
}
11991202
c.emitInstOp(opEnd)
12001203
}
12011204

1202-
func (c *compiler) compileStmtSlice(stmts stmtSlice) {
1205+
func (c *compiler) compileStmtSlice(stmts []ast.Stmt) {
12031206
c.emitInstOp(opMultiStmt)
12041207
insideStmtList := c.insideStmtList
12051208
c.insideStmtList = true
@@ -1210,7 +1213,7 @@ func (c *compiler) compileStmtSlice(stmts stmtSlice) {
12101213
c.emitInstOp(opEnd)
12111214
}
12121215

1213-
func (c *compiler) compileExprSlice(exprs ExprSlice) {
1216+
func (c *compiler) compileExprSlice(exprs []ast.Expr) {
12141217
c.emitInstOp(opMultiExpr)
12151218
for _, n := range exprs {
12161219
c.compileExpr(n)

gogrep.go

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func IsEmptyNodeSlice(n ast.Node) bool {
14-
if list, ok := n.(NodeSlice); ok {
14+
if list, ok := n.(*NodeSlice); ok {
1515
return list.Len() == 0
1616
}
1717
return false
@@ -62,14 +62,18 @@ type MatcherState struct {
6262
// actual matching phase)
6363
capture []CapturedNode
6464

65+
nodeSlices []NodeSlice
66+
nodeSlicesUsed int
67+
6568
pc int
6669

6770
partial PartialNode
6871
}
6972

7073
func NewMatcherState() MatcherState {
7174
return MatcherState{
72-
capture: make([]CapturedNode, 0, 8),
75+
capture: make([]CapturedNode, 0, 8),
76+
nodeSlices: make([]NodeSlice, 16),
7377
}
7478
}
7579

@@ -143,34 +147,37 @@ func Compile(config CompileConfig) (*Pattern, PatternInfo, error) {
143147
}
144148

145149
func Walk(root ast.Node, fn func(n ast.Node) bool) {
146-
switch root := root.(type) {
147-
case ExprSlice:
148-
for _, e := range root {
149-
ast.Inspect(e, fn)
150-
}
151-
case stmtSlice:
152-
for _, e := range root {
153-
ast.Inspect(e, fn)
154-
}
155-
case fieldSlice:
156-
for _, e := range root {
157-
ast.Inspect(e, fn)
158-
}
159-
case identSlice:
160-
for _, e := range root {
161-
ast.Inspect(e, fn)
150+
if root, ok := root.(*NodeSlice); ok {
151+
switch root.Kind {
152+
case ExprNodeSlice:
153+
for _, e := range root.exprSlice {
154+
ast.Inspect(e, fn)
155+
}
156+
case StmtNodeSlice:
157+
for _, e := range root.stmtSlice {
158+
ast.Inspect(e, fn)
159+
}
160+
case FieldNodeSlice:
161+
for _, e := range root.fieldSlice {
162+
ast.Inspect(e, fn)
163+
}
164+
case IdentNodeSlice:
165+
for _, e := range root.identSlice {
166+
ast.Inspect(e, fn)
167+
}
168+
case SpecNodeSlice:
169+
for _, e := range root.specSlice {
170+
ast.Inspect(e, fn)
171+
}
172+
default:
173+
for _, e := range root.declSlice {
174+
ast.Inspect(e, fn)
175+
}
162176
}
163-
case specSlice:
164-
for _, e := range root {
165-
ast.Inspect(e, fn)
166-
}
167-
case declSlice:
168-
for _, e := range root {
169-
ast.Inspect(e, fn)
170-
}
171-
default:
172-
ast.Inspect(root, fn)
177+
return
173178
}
179+
180+
ast.Inspect(root, fn)
174181
}
175182

176183
func newPatternInfo() PatternInfo {

0 commit comments

Comments
 (0)