1 package wasi_snapshot_preview1_test
2
3 import (
4 "embed"
5 "io/fs"
6 "os"
7 "testing"
8
9 "github.com/tetratelabs/wazero"
10 "github.com/tetratelabs/wazero/api"
11 experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
12 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
13 "github.com/tetratelabs/wazero/internal/sys"
14 "github.com/tetratelabs/wazero/internal/testing/proxy"
15 "github.com/tetratelabs/wazero/internal/wasip1"
16 "github.com/tetratelabs/wazero/internal/wasm"
17 )
18
19
20 var configArgsEnviron = wazero.NewModuleConfig().
21 WithArgs("aa=bbbb", "cccccc=dddddddd", "eeeeeeeeee=ffffffffffff").
22 WithEnv("aa", "bbbb").
23 WithEnv("cccccc", "dddddddd").
24 WithEnv("eeeeeeeeee", "ffffffffffff")
25
26 func Benchmark_ArgsEnviron(b *testing.B) {
27 r := wazero.NewRuntime(testCtx)
28 defer r.Close(testCtx)
29
30 mod, err := instantiateProxyModule(r, configArgsEnviron)
31 if err != nil {
32 b.Fatal(err)
33 }
34
35 for _, n := range []string{
36 wasip1.ArgsGetName,
37 wasip1.ArgsSizesGetName,
38 wasip1.EnvironGetName,
39 wasip1.EnvironSizesGetName,
40 } {
41 n := n
42 fn := mod.ExportedFunction(n)
43 b.Run(n, func(b *testing.B) {
44 b.ReportAllocs()
45 for i := 0; i < b.N; i++ {
46 results, err := fn.Call(testCtx, uint64(0), uint64(4))
47 if err != nil {
48 b.Fatal(err)
49 }
50 requireESuccess(b, results)
51 }
52 })
53 }
54 }
55
56 type money struct{}
57
58
59 func (money) Read(b []byte) (n int, err error) {
60 for i := range b {
61 b[i] = '$'
62 }
63
64 return len(b), nil
65 }
66
67 func Benchmark_fdRead(b *testing.B) {
68 r := wazero.NewRuntime(testCtx)
69 defer r.Close(testCtx)
70
71 mod, err := instantiateProxyModule(r, wazero.NewModuleConfig().WithStdin(money{}))
72 if err != nil {
73 b.Fatal(err)
74 }
75 fn := mod.ExportedFunction(wasip1.FdReadName)
76
77 mod.Memory().Write(0, []byte{
78 32, 0, 0, 0,
79 8, 0, 0, 0,
80 40, 0, 0, 0,
81 8, 0, 0, 0,
82 48, 0, 0, 0,
83 16, 0, 0, 0,
84 64, 0, 0, 0,
85 16, 0, 0, 0,
86 })
87
88 benches := []struct {
89 name string
90 iovs uint32
91 iovsCount uint32
92 }{
93 {
94 name: "1x8",
95 iovs: 0,
96 iovsCount: 1,
97 },
98 {
99 name: "2x16",
100 iovs: 16,
101 iovsCount: 2,
102 },
103 }
104
105 for _, bb := range benches {
106 bc := bb
107
108 b.ReportAllocs()
109 b.Run(bc.name, func(b *testing.B) {
110 resultNread := uint32(128)
111
112 for i := 0; i < b.N; i++ {
113 results, err := fn.Call(testCtx, uint64(0), uint64(bc.iovs), uint64(bc.iovsCount), uint64(resultNread))
114 if err != nil {
115 b.Fatal(err)
116 }
117 requireESuccess(b, results)
118 }
119 })
120 }
121 }
122
123
124 var testdata embed.FS
125
126 func Benchmark_fdReaddir(b *testing.B) {
127 embedFS, err := fs.Sub(testdata, "testdata")
128 if err != nil {
129 b.Fatal(err)
130 }
131
132 benches := []struct {
133 name string
134 fs fs.FS
135
136 dirMount string
137
138 twoCalls bool
139 }{
140 {
141 name: "embed.FS",
142 fs: embedFS,
143 },
144 {
145 name: "embed.FS - two calls",
146 fs: embedFS,
147 twoCalls: true,
148 },
149 {
150 name: "os.DirFS",
151 fs: os.DirFS("testdata"),
152 },
153 {
154 name: "os.DirFS - two calls",
155 fs: os.DirFS("testdata"),
156 twoCalls: true,
157 },
158 {
159 name: "sysfs.DirFS",
160 dirMount: "testdata",
161 },
162 {
163 name: "sysfs.DirFS - two calls",
164 dirMount: "testdata",
165 twoCalls: true,
166 },
167 }
168
169 for _, bb := range benches {
170 bc := bb
171
172 b.Run(bc.name, func(b *testing.B) {
173 r := wazero.NewRuntime(testCtx)
174 defer r.Close(testCtx)
175
176 fsConfig := wazero.NewFSConfig()
177 if bc.fs != nil {
178 fsConfig = fsConfig.WithFSMount(bc.fs, "")
179 } else {
180 fsConfig = fsConfig.WithDirMount(bc.dirMount, "")
181 }
182
183 mod, err := instantiateProxyModule(r, wazero.NewModuleConfig().WithFSConfig(fsConfig))
184 if err != nil {
185 b.Fatal(err)
186 }
187
188 fn := mod.ExportedFunction(wasip1.FdReaddirName)
189
190 b.ResetTimer()
191 b.ReportAllocs()
192 for i := 0; i < b.N; i++ {
193 b.StopTimer()
194
195 cookie := 0
196 resultBufused := 0
197 buf := 8
198 bufLen := 8096
199
200
201 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
202 fd, errno := fsc.OpenFile(fsc.RootFS(), ".", experimentalsys.O_RDONLY, 0)
203 if errno != 0 {
204 b.Fatal(errno)
205 }
206
207
208 b.StartTimer()
209
210 if bc.twoCalls {
211
212 bufLen := wasip1.DirentSize + 1
213 bufLen += wasip1.DirentSize + 2
214
215 results, err := fn.Call(testCtx, uint64(fd), uint64(buf), uint64(bufLen), uint64(cookie), uint64(resultBufused))
216 if err != nil {
217 b.Fatal(err)
218 }
219 requireESuccess(b, results)
220 cookie = 2
221 }
222
223 results, err := fn.Call(testCtx, uint64(fd), uint64(buf), uint64(bufLen), uint64(cookie), uint64(resultBufused))
224 if err != nil {
225 b.Fatal(err)
226 }
227 b.StopTimer()
228
229 requireESuccess(b, results)
230 if errno = fsc.CloseFile(fd); errno != 0 {
231 b.Fatal(errno)
232 }
233 }
234 })
235 }
236 }
237
238 func Benchmark_pathFilestat(b *testing.B) {
239 embedFS, err := fs.Sub(testdata, "testdata")
240 if err != nil {
241 b.Fatal(err)
242 }
243
244 benches := []struct {
245 name string
246 fs fs.FS
247
248 dirMount string
249 path string
250 fd int32
251 }{
252 {
253 name: "embed.FS fd=root",
254 fs: embedFS,
255 path: "zig",
256 fd: sys.FdPreopen,
257 },
258 {
259 name: "embed.FS fd=directory",
260 fs: embedFS,
261 path: "wasi.zig",
262 },
263 {
264 name: "os.DirFS fd=root",
265 fs: os.DirFS("testdata"),
266 path: "zig",
267 fd: sys.FdPreopen,
268 },
269 {
270 name: "os.DirFS fd=directory",
271 fs: os.DirFS("testdata"),
272 path: "wasi.zig",
273 },
274 {
275 name: "sysfs.DirFS fd=root",
276 dirMount: "testdata",
277 path: "zig",
278 fd: sys.FdPreopen,
279 },
280 {
281 name: "sysfs.DirFS fd=directory",
282 dirMount: "testdata",
283 path: "wasi.zig",
284 },
285 }
286
287 for _, bb := range benches {
288 bc := bb
289
290 b.Run(bc.name, func(b *testing.B) {
291 r := wazero.NewRuntime(testCtx)
292 defer r.Close(testCtx)
293
294 fsConfig := wazero.NewFSConfig()
295 if bc.fs != nil {
296 fsConfig = fsConfig.WithFSMount(bc.fs, "")
297 } else {
298 fsConfig = fsConfig.WithDirMount(bc.dirMount, "")
299 }
300
301 mod, err := instantiateProxyModule(r, wazero.NewModuleConfig().WithFSConfig(fsConfig))
302 if err != nil {
303 b.Fatal(err)
304 }
305
306
307
308 fd := sys.FdPreopen
309 if bc.fd != sys.FdPreopen {
310 fsc := mod.(*wasm.ModuleInstance).Sys.FS()
311 fd, errno := fsc.OpenFile(fsc.RootFS(), "zig", experimentalsys.O_RDONLY, 0)
312 if errno != 0 {
313 b.Fatal(errno)
314 }
315 defer fsc.CloseFile(fd)
316 }
317
318 fn := mod.ExportedFunction(wasip1.PathFilestatGetName)
319
320 b.ResetTimer()
321 b.ReportAllocs()
322 for i := 0; i < b.N; i++ {
323 b.StopTimer()
324
325 flags := uint32(0)
326 path := uint32(0)
327 pathLen := len(bc.path)
328 resultFilestat := 1024
329
330 if !mod.Memory().WriteString(path, bc.path) {
331 b.Fatal("could not write path")
332 }
333
334
335 b.StartTimer()
336 results, err := fn.Call(testCtx, uint64(fd), uint64(flags), uint64(path), uint64(pathLen), uint64(resultFilestat))
337 if err != nil {
338 b.Fatal(err)
339 }
340 b.StopTimer()
341
342 requireESuccess(b, results)
343 }
344 })
345 }
346 }
347
348 func requireESuccess(b *testing.B, results []uint64) {
349 b.Helper()
350 if errno := wasip1.Errno(results[0]); errno != 0 {
351 b.Fatal(wasip1.ErrnoName(errno))
352 }
353 }
354
355 type writerFunc func(buf []byte) (n int, err error)
356
357
358 func (f writerFunc) Write(buf []byte) (n int, err error) {
359 return f(buf)
360 }
361
362 func Benchmark_fdWrite(b *testing.B) {
363 r := wazero.NewRuntime(testCtx)
364 defer r.Close(testCtx)
365
366 mod, err := instantiateProxyModule(r, wazero.NewModuleConfig().
367 WithStdout(writerFunc(func(buf []byte) (n int, err error) { return len(buf), nil })),
368 )
369 if err != nil {
370 b.Fatal(err)
371 }
372 fn := mod.ExportedFunction(wasip1.FdWriteName)
373
374 iovs := uint32(1)
375 mod.Memory().Write(0, []byte{
376 '?',
377 18, 0, 0, 0,
378 4, 0, 0, 0,
379 23, 0, 0, 0,
380 2, 0, 0, 0,
381 '?',
382 'w', 'a', 'z', 'e',
383 '?',
384 'r', 'o',
385 '?',
386 })
387
388 iovsCount := uint32(2)
389 resultNwritten := uint32(26)
390
391 benches := []struct {
392 name string
393 fd int32
394 }{
395 {
396 name: "io.Writer",
397 fd: sys.FdStdout,
398 },
399 {
400 name: "io.Discard",
401 fd: sys.FdStderr,
402 },
403 }
404
405 for _, bb := range benches {
406 bc := bb
407
408 b.ReportAllocs()
409 b.Run(bc.name, func(b *testing.B) {
410 for i := 0; i < b.N; i++ {
411 results, err := fn.Call(testCtx, uint64(bc.fd), uint64(iovs), uint64(iovsCount), uint64(resultNwritten))
412 if err != nil {
413 b.Fatal(err)
414 }
415 requireESuccess(b, results)
416 }
417 })
418 }
419 }
420
421
422 func instantiateProxyModule(r wazero.Runtime, config wazero.ModuleConfig) (api.Module, error) {
423 wasiModuleCompiled, err := wasi_snapshot_preview1.NewBuilder(r).Compile(testCtx)
424 if err != nil {
425 return nil, err
426 }
427
428 if _, err = r.InstantiateModule(testCtx, wasiModuleCompiled, wazero.NewModuleConfig()); err != nil {
429 return nil, err
430 }
431
432 proxyBin := proxy.NewModuleBinary(wasi_snapshot_preview1.ModuleName, wasiModuleCompiled)
433
434 proxyCompiled, err := r.CompileModule(testCtx, proxyBin)
435 if err != nil {
436 return nil, err
437 }
438
439 return r.InstantiateModule(testCtx, proxyCompiled, config)
440 }
441
View as plain text