forked from google/starlark-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bench_test.go
172 lines (152 loc) · 4.97 KB
/
bench_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// Copyright 2018 The Bazel Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package starlark_test
import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"testing"
"go.starlark.net/lib/json"
"go.starlark.net/starlark"
"go.starlark.net/starlarktest"
)
func BenchmarkStarlark(b *testing.B) {
defer setOptions("")
starlark.Universe["json"] = json.Module
testdata := starlarktest.DataFile("starlark", ".")
thread := new(starlark.Thread)
for _, file := range []string{
"testdata/benchmark.star",
// ...
} {
filename := filepath.Join(testdata, file)
src, err := ioutil.ReadFile(filename)
if err != nil {
b.Error(err)
continue
}
setOptions(string(src))
// Evaluate the file once.
globals, err := starlark.ExecFile(thread, filename, src, nil)
if err != nil {
reportEvalError(b, err)
}
// Repeatedly call each global function named bench_* as a benchmark.
for _, name := range globals.Keys() {
value := globals[name]
if fn, ok := value.(*starlark.Function); ok && strings.HasPrefix(name, "bench_") {
b.Run(name, func(b *testing.B) {
_, err := starlark.Call(thread, fn, starlark.Tuple{benchmark{b}}, nil)
if err != nil {
reportEvalError(b, err)
}
})
}
}
}
}
// A benchmark is passed to each bench_xyz(b) function in a bench_*.star file.
// It provides b.n, the number of iterations that must be executed by the function,
// which is typically of the form:
//
// def bench_foo(b):
// for _ in range(b.n):
// ...work...
//
// It also provides stop, start, and restart methods to stop the clock in case
// there is significant set-up work that should not count against the measured
// operation.
//
// (This interface is inspired by Go's testing.B, and is also implemented
// by the java.starlark.net implementation; see
// https://github.com/bazelbuild/starlark/pull/75#pullrequestreview-275604129.)
type benchmark struct {
b *testing.B
}
func (benchmark) Freeze() {}
func (benchmark) Truth() starlark.Bool { return true }
func (benchmark) Type() string { return "benchmark" }
func (benchmark) String() string { return "<benchmark>" }
func (benchmark) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: benchmark") }
func (benchmark) AttrNames() []string { return []string{"n", "restart", "start", "stop"} }
func (b benchmark) Attr(name string) (starlark.Value, error) {
switch name {
case "n":
return starlark.MakeInt(b.b.N), nil
case "restart":
return benchmarkRestart.BindReceiver(b), nil
case "start":
return benchmarkStart.BindReceiver(b), nil
case "stop":
return benchmarkStop.BindReceiver(b), nil
}
return nil, nil
}
var (
benchmarkRestart = starlark.NewBuiltin("restart", benchmarkRestartImpl)
benchmarkStart = starlark.NewBuiltin("start", benchmarkStartImpl)
benchmarkStop = starlark.NewBuiltin("stop", benchmarkStopImpl)
)
func benchmarkRestartImpl(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
b.Receiver().(benchmark).b.ResetTimer()
return starlark.None, nil
}
func benchmarkStartImpl(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
b.Receiver().(benchmark).b.StartTimer()
return starlark.None, nil
}
func benchmarkStopImpl(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
b.Receiver().(benchmark).b.StopTimer()
return starlark.None, nil
}
// 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).
filename := starlarktest.DataFile("starlark", "testdata/paths.star")
var src []byte
b.Run("read", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var err error
src, err = ioutil.ReadFile(filename)
if err != nil {
b.Fatal(err)
}
}
})
// Measure time to turn a source filename into a compiled program (approx 450us).
var prog *starlark.Program
b.Run("compile", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var err error
_, prog, err = starlark.SourceProgram(filename, src, starlark.StringDict(nil).Has)
if err != nil {
b.Fatal(err)
}
}
})
// Measure time to encode a compiled program to a memory buffer
// (approx 20us; was 75-120us with gob encoding).
var out bytes.Buffer
b.Run("encode", func(b *testing.B) {
for i := 0; i < b.N; i++ {
out.Reset()
if err := prog.Write(&out); err != nil {
b.Fatal(err)
}
}
})
// Measure time to decode a compiled program from a memory buffer
// (approx 20us; was 135-250us with gob encoding)
b.Run("decode", func(b *testing.B) {
for i := 0; i < b.N; i++ {
in := bytes.NewReader(out.Bytes())
if _, err := starlark.CompiledProgram(in); err != nil {
b.Fatal(err)
}
}
})
}