syntax: add benchmarks for scanning and parsing
Change-Id: Ie1269b8899c4454dc955c7453ed9e9266aceaaa3
diff --git a/starlark/bench_test.go b/starlark/bench_test.go
index 7f78a22..1495291 100644
--- a/starlark/bench_test.go
+++ b/starlark/bench_test.go
@@ -13,6 +13,7 @@
"go.starlark.net/starlark"
"go.starlark.net/starlarktest"
+ "go.starlark.net/syntax"
)
func Benchmark(b *testing.B) {
@@ -58,10 +59,11 @@
}
// BenchmarkProgram measures operations relevant to compiled programs.
-// TODO(adonovan): use a bigger testdata program.
func BenchmarkProgram(b *testing.B) {
- // Measure time to read a source file (approx 600us but depends on hardware and file system).
+ // TODO(adonovan): use a bigger testdata program.
filename := starlarktest.DataFile("starlark", "testdata/paths.star")
+
+ // Measure time to read a source file (approx 600us but depends on hardware and file system).
var src []byte
b.Run("read", func(b *testing.B) {
for i := 0; i < b.N; i++ {
@@ -73,7 +75,26 @@
}
})
- // Measure time to turn a source filename into a compiled program (approx 450us).
+ // Measure time to scan (approx 170us).
+ b.Run("scan", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if err := syntax.ScanAndDiscard(filename, src, 0); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+
+ // Measure time to parse (approx 300us).
+ b.Run("parse", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ if _, err := syntax.Parse(filename, src, 0); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+
+ // Measure time to turn a source filename into a compiled program,
+ // that is, read + scan + parse + resolve + compile (approx 450us).
var prog *starlark.Program
b.Run("compile", func(b *testing.B) {
for i := 0; i < b.N; i++ {
diff --git a/syntax/parse.go b/syntax/parse.go
index 0e4d284..7282856 100644
--- a/syntax/parse.go
+++ b/syntax/parse.go
@@ -47,6 +47,23 @@
return f, nil
}
+// ScanAndDiscard tokenizes the input data and discards the tokens.
+// Parameters are as for Parse.
+// It exists only for internal benchmarking purposes.
+func ScanAndDiscard(filename string, src interface{}, mode Mode) error {
+ in, err := newScanner(filename, src, mode&RetainComments != 0)
+ if err != nil {
+ return err
+ }
+ p := parser{in: in}
+ defer p.in.recover(&err)
+ p.nextToken() // read first lookahead token
+ for p.tok != EOF {
+ p.nextToken()
+ }
+ return nil
+}
+
// ParseCompoundStmt parses a single compound statement:
// a blank line, a def, for, while, or if statement, or a
// semicolon-separated list of simple statements followed