github.com/tetratelabs/wazero@v1.7.1/internal/integration_test/stdlibs/bench_test.go (about) 1 package wazevo_test 2 3 import ( 4 "context" 5 "crypto/rand" 6 "io" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "testing" 12 13 "github.com/tetratelabs/wazero" 14 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 15 "github.com/tetratelabs/wazero/internal/testing/require" 16 "github.com/tetratelabs/wazero/sys" 17 ) 18 19 func BenchmarkZig(b *testing.B) { 20 c := wazero.NewRuntimeConfigCompiler() 21 runtBenches(b, context.Background(), c, zigTestCase) 22 } 23 24 func BenchmarkTinyGo(b *testing.B) { 25 c := wazero.NewRuntimeConfigCompiler() 26 runtBenches(b, context.Background(), c, tinyGoTestCase) 27 } 28 29 func BenchmarkWasip1(b *testing.B) { 30 c := wazero.NewRuntimeConfigCompiler() 31 runtBenches(b, context.Background(), c, wasip1TestCase) 32 } 33 34 type testCase struct { 35 name, dir string 36 readTestCase func(fpath string, fname string) (_ []byte, c wazero.ModuleConfig, stdout, stderr *os.File, err error) 37 } 38 39 var ( 40 zigTestCase = testCase{ 41 name: "zig", 42 dir: "testdata/zig/", 43 readTestCase: func(fpath string, fname string) (_ []byte, c wazero.ModuleConfig, stdout, stderr *os.File, err error) { 44 bin, err := os.ReadFile(fpath) 45 c, stdout, stderr = defaultModuleConfig() 46 c = c.WithFSConfig(wazero.NewFSConfig().WithDirMount(".", "/")). 47 WithArgs("test.wasm") 48 return bin, c, stdout, stderr, err 49 }, 50 } 51 tinyGoTestCase = testCase{ 52 name: "tinygo", 53 dir: "testdata/tinygo/", 54 readTestCase: func(fpath string, fname string) (_ []byte, c wazero.ModuleConfig, stdout, stderr *os.File, err error) { 55 if !strings.HasSuffix(fname, ".test") { 56 return nil, nil, nil, nil, nil 57 } 58 bin, err := os.ReadFile(fpath) 59 60 fsconfig := wazero.NewFSConfig(). 61 WithDirMount(".", "/"). 62 WithDirMount(os.TempDir(), "/tmp") 63 64 c, stdout, stderr = defaultModuleConfig() 65 c = c.WithFSConfig(fsconfig). 66 WithArgs(fname, "-test.v") 67 68 return bin, c, stdout, stderr, err 69 }, 70 } 71 wasip1TestCase = testCase{ 72 name: "wasip1", 73 dir: "testdata/go/", 74 readTestCase: func(fpath string, fname string) (_ []byte, c wazero.ModuleConfig, stdout, stderr *os.File, err error) { 75 if !strings.HasSuffix(fname, ".test") { 76 return nil, nil, nil, nil, nil 77 } 78 bin, err := os.ReadFile(fpath) 79 if err != nil { 80 return nil, nil, nil, nil, err 81 } 82 fsuffixstripped := strings.ReplaceAll(fname, ".test", "") 83 inferredpath := strings.ReplaceAll(fsuffixstripped, "_", "/") 84 testdir := filepath.Join(runtime.GOROOT(), inferredpath) 85 err = os.Chdir(testdir) 86 87 sysroot := filepath.VolumeName(testdir) + string(os.PathSeparator) 88 normalizedTestdir := normalizeOsPath(testdir) 89 90 c, stdout, stderr = defaultModuleConfig() 91 c = c.WithFSConfig( 92 wazero.NewFSConfig(). 93 WithDirMount(sysroot, "/")). 94 WithEnv("PWD", normalizedTestdir) 95 96 args := []string{fname, "-test.short", "-test.v"} 97 98 // Skip tests that are fragile on Windows. 99 if runtime.GOOS == "windows" { 100 c = c. 101 WithEnv("GOROOT", normalizeOsPath(runtime.GOROOT())) 102 103 args = append(args, 104 "-test.skip=TestRenameCaseDifference/dir|"+ 105 "TestDirFSPathsValid|TestDirFS|TestDevNullFile|"+ 106 "TestOpenError|TestSymlinkWithTrailingSlash") 107 } 108 c = c.WithArgs(args...) 109 110 return bin, c, stdout, stderr, err 111 }, 112 } 113 ) 114 115 func runtBenches(b *testing.B, ctx context.Context, rc wazero.RuntimeConfig, tc testCase) { 116 cwd, _ := os.Getwd() 117 files, err := os.ReadDir(tc.dir) 118 require.NoError(b, err) 119 for _, f := range files { 120 fname := f.Name() 121 // Ensure we are on root dir. 122 err = os.Chdir(cwd) 123 require.NoError(b, err) 124 125 fpath := filepath.Join(cwd, tc.dir, fname) 126 bin, modCfg, stdout, stderr, err := tc.readTestCase(fpath, fname) 127 require.NoError(b, err) 128 if bin == nil { 129 continue 130 } 131 132 b.Run("Compile/"+fname, func(b *testing.B) { 133 b.ResetTimer() 134 for i := 0; i < b.N; i++ { 135 r := wazero.NewRuntimeWithConfig(ctx, rc) 136 _, err := r.CompileModule(ctx, bin) 137 require.NoError(b, err) 138 require.NoError(b, r.Close(ctx)) 139 } 140 }) 141 b.Run("Run/"+fname, func(b *testing.B) { 142 r := wazero.NewRuntimeWithConfig(ctx, rc) 143 wasi_snapshot_preview1.MustInstantiate(ctx, r) 144 b.Cleanup(func() { r.Close(ctx) }) 145 146 cm, err := r.CompileModule(ctx, bin) 147 require.NoError(b, err) 148 149 b.ResetTimer() 150 for i := 0; i < b.N; i++ { 151 // Instantiate in the loop as _start cannot be called multiple times. 152 m, err := r.InstantiateModule(ctx, cm, modCfg) 153 requireZeroExitCode(b, err, stdout, stderr) 154 require.NoError(b, m.Close(ctx)) 155 } 156 }) 157 } 158 } 159 160 // Normalize an absolute path to a Unix-style path, regardless if it is a Windows path. 161 func normalizeOsPath(path string) string { 162 // Remove volume name. This is '/' on *Nix and 'C:' (with C being any letter identifier). 163 root := filepath.VolumeName(path) 164 testdirnoprefix := path[len(root):] 165 // Normalizes all the path separators to a Unix separator. 166 testdirnormalized := strings.ReplaceAll(testdirnoprefix, string(os.PathSeparator), "/") 167 return testdirnormalized 168 } 169 170 func defaultModuleConfig() (c wazero.ModuleConfig, stdout, stderr *os.File) { 171 var err error 172 // Note: do not use os.Stdout or os.Stderr as they will mess up the `-bench` output to be fed to the benchstat tool. 173 stdout, err = os.CreateTemp("", "") 174 if err != nil { 175 panic(err) 176 } 177 stderr, err = os.CreateTemp("", "") 178 if err != nil { 179 panic(err) 180 } 181 c = wazero.NewModuleConfig(). 182 WithSysNanosleep(). 183 WithSysNanotime(). 184 WithSysWalltime(). 185 WithRandSource(rand.Reader). 186 // Some tests require Stdout and Stderr to be present. 187 WithStdout(stdout). 188 WithStderr(stderr) 189 return 190 } 191 192 func requireZeroExitCode(b *testing.B, err error, stdout, stderr *os.File) { 193 b.Helper() 194 if se, ok := err.(*sys.ExitError); ok { 195 if se.ExitCode() != 0 { // Don't err on success. 196 stdoutBytes, _ := io.ReadAll(stdout) 197 stderrBytes, _ := io.ReadAll(stderr) 198 require.NoError(b, err, "stdout: %s\nstderr: %s", string(stdoutBytes), string(stderrBytes)) 199 } 200 } else if err != nil { 201 stdoutBytes, _ := io.ReadAll(stdout) 202 stderrBytes, _ := io.ReadAll(stderr) 203 require.NoError(b, err, "stdout: %s\nstderr: %s", string(stdoutBytes), string(stderrBytes)) 204 } 205 }