1 package vs
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "path"
8 "runtime"
9 "sort"
10 "testing"
11 "text/tabwriter"
12
13 "github.com/tetratelabs/wazero/internal/testing/require"
14 )
15
16
17 var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary")
18
19
20
21
22 var ensureCompilerFastest = "false"
23
24 const compilerRuntime = "wazero-compiler"
25
26
27
28 func runTestBenchmark_Call_CompilerFastest(t *testing.T, rtCfg *RuntimeConfig, name string, call func(Module, int) error, vsRuntime Runtime) {
29 if ensureCompilerFastest != "true" {
30 t.Skip()
31 }
32
33 type benchResult struct {
34 name string
35 nsOp float64
36 }
37
38 results := make([]benchResult, 0, 2)
39
40 compilerNsOp := runCallBenchmark(NewWazeroCompilerRuntime(), rtCfg, call)
41 results = append(results, benchResult{name: compilerRuntime, nsOp: compilerNsOp})
42
43
44 vsNsOp := runCallBenchmark(vsRuntime, rtCfg, call)
45 results = append(results, benchResult{name: vsRuntime.Name(), nsOp: vsNsOp})
46
47 sort.Slice(results, func(i, j int) bool {
48 return results[i].nsOp < results[j].nsOp
49 })
50
51
52 w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
53 _, _ = fmt.Fprintf(w, "Benchmark%s/Call-16\n", name)
54 for _, result := range results {
55 _, _ = fmt.Fprintf(w, "%s\t%.2f\tns/op\n", result.name, result.nsOp)
56 }
57 _ = w.Flush()
58
59
60 require.Equal(t, compilerRuntime, results[0].name, "%s is faster than %s. "+
61 "Run with ensureCompilerFastest=false instead to see the detailed result",
62 results[0].name, compilerRuntime)
63 }
64
65 func runCallBenchmark(rt Runtime, rtCfg *RuntimeConfig, call func(Module, int) error) float64 {
66 result := testing.Benchmark(func(b *testing.B) {
67 benchmarkCall(b, rt, rtCfg, call)
68 })
69
70 nsOp := float64(result.T.Nanoseconds()) / float64(result.N)
71 return nsOp
72 }
73
74 func benchmark(b *testing.B, runtime func() Runtime, rtCfg *RuntimeConfig, call func(Module, int) error) {
75 rt := runtime()
76 b.Run("Compile", func(b *testing.B) {
77 b.ReportAllocs()
78 benchmarkCompile(b, rt, rtCfg)
79 })
80 b.Run("Instantiate", func(b *testing.B) {
81 b.ReportAllocs()
82 benchmarkInstantiate(b, rt, rtCfg)
83 })
84
85
86 if call != nil && (ensureCompilerFastest != "true" || rt.Name() == compilerRuntime) {
87 b.Run("Call", func(b *testing.B) {
88 b.ReportAllocs()
89 benchmarkCall(b, rt, rtCfg, call)
90 })
91 }
92 }
93
94 func benchmarkCompile(b *testing.B, rt Runtime, rtCfg *RuntimeConfig) {
95 for i := 0; i < b.N; i++ {
96 if err := rt.Compile(testCtx, rtCfg); err != nil {
97 b.Fatal(err)
98 }
99 if err := rt.Close(testCtx); err != nil {
100 b.Fatal(err)
101 }
102 }
103 }
104
105 func benchmarkInstantiate(b *testing.B, rt Runtime, rtCfg *RuntimeConfig) {
106
107 if err := rt.Compile(testCtx, rtCfg); err != nil {
108 b.Fatal(err)
109 }
110 defer rt.Close(testCtx)
111
112 b.ResetTimer()
113 for i := 0; i < b.N; i++ {
114 mod, err := rt.Instantiate(testCtx, rtCfg)
115 if err != nil {
116 b.Fatal(err)
117 }
118 err = mod.Close(testCtx)
119 if err != nil {
120 b.Fatal(err)
121 }
122 }
123 }
124
125 func benchmarkCall(b *testing.B, rt Runtime, rtCfg *RuntimeConfig, call func(Module, int) error) {
126
127 if err := rt.Compile(testCtx, rtCfg); err != nil {
128 b.Fatal(err)
129 }
130 defer rt.Close(testCtx)
131 mod, err := rt.Instantiate(testCtx, rtCfg)
132 if err != nil {
133 b.Fatal(err)
134 }
135 defer mod.Close(testCtx)
136 b.ResetTimer()
137 for i := 0; i < b.N; i++ {
138 if err := call(mod, i); err != nil {
139 b.Fatal(err)
140 }
141 }
142 }
143
144 func testCall(t *testing.T, runtime func() Runtime, rtCfg *RuntimeConfig, testCall func(*testing.T, Module, int, int)) {
145 rt := runtime()
146 err := rt.Compile(testCtx, rtCfg)
147 require.NoError(t, err)
148 defer rt.Close(testCtx)
149
150
151 for i := 0; i < 10; i++ {
152 m, err := rt.Instantiate(testCtx, rtCfg)
153 require.NoError(t, err)
154
155
156 for j := 0; j < 1000; j++ {
157 testCall(t, m, i, j)
158 }
159
160 require.NoError(t, m.Close(testCtx))
161 }
162 }
163
164 func testInstantiate(t *testing.T, runtime func() Runtime, rtCfg *RuntimeConfig) {
165 rt := runtime()
166 err := rt.Compile(testCtx, rtCfg)
167 require.NoError(t, err)
168 defer rt.Close(testCtx)
169
170
171 for i := 0; i < 10; i++ {
172 m, err := rt.Instantiate(testCtx, rtCfg)
173 require.NoError(t, err)
174 require.NoError(t, m.Close(testCtx))
175 }
176 }
177
178 func readRelativeFile(relativePath string) []byte {
179
180 _, source, _, _ := runtime.Caller(1)
181 realPath := path.Join(path.Dir(source), relativePath)
182 bytes, err := os.ReadFile(realPath)
183 if err != nil {
184 panic(err)
185 }
186 return bytes
187 }
188
View as plain text