gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/tmpfs/benchmark_test.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package benchmark_test 16 17 import ( 18 "fmt" 19 "runtime" 20 "strings" 21 "testing" 22 23 "gvisor.dev/gvisor/pkg/abi/linux" 24 "gvisor.dev/gvisor/pkg/fspath" 25 "gvisor.dev/gvisor/pkg/refs" 26 "gvisor.dev/gvisor/pkg/sentry/contexttest" 27 "gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs" 28 "gvisor.dev/gvisor/pkg/sentry/kernel/auth" 29 "gvisor.dev/gvisor/pkg/sentry/vfs" 30 ) 31 32 // Differences from stat_benchmark: 33 // 34 // - Syscall interception, CopyInPath, copyOutStat, and overlayfs overheads are 35 // not included. 36 // 37 // - *MountStat benchmarks use a tmpfs root mount and a tmpfs submount at /tmp. 38 // 39 // Non-MountStat benchmarks use a tmpfs root mount and no submounts. 40 // stat_benchmark uses a varying root mount, a tmpfs submount at /tmp, and a 41 // subdirectory /tmp/<top_dir> (assuming TEST_TMPDIR == "/tmp"). Thus 42 // stat_benchmark at depth 1 does a comparable amount of work to *MountStat 43 // benchmarks at depth 2, and non-MountStat benchmarks at depth 3. 44 var depths = []int{1, 2, 3, 8, 64, 100} 45 46 const ( 47 mountPointName = "tmp" 48 filename = "gvisor_test_temp_0_1557494568" 49 ) 50 51 func BenchmarkTmpfsStat(b *testing.B) { 52 for _, depth := range depths { 53 b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { 54 ctx := contexttest.Context(b) 55 creds := auth.CredentialsFromContext(ctx) 56 57 // Create VFS. 58 vfsObj := vfs.VirtualFilesystem{} 59 if err := vfsObj.Init(ctx); err != nil { 60 b.Fatalf("VFS init: %v", err) 61 } 62 vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ 63 AllowUserMount: true, 64 }) 65 mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil) 66 if err != nil { 67 b.Fatalf("failed to create tmpfs root mount: %v", err) 68 } 69 defer mntns.DecRef(ctx) 70 71 var filePathBuilder strings.Builder 72 filePathBuilder.WriteByte('/') 73 74 // Create nested directories with given depth. 75 root := mntns.Root(ctx) 76 defer root.DecRef(ctx) 77 vd := root 78 vd.IncRef() 79 for i := depth; i > 0; i-- { 80 name := fmt.Sprintf("%d", i) 81 pop := vfs.PathOperation{ 82 Root: root, 83 Start: vd, 84 Path: fspath.Parse(name), 85 } 86 if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{ 87 Mode: 0755, 88 }); err != nil { 89 b.Fatalf("failed to create directory %q: %v", name, err) 90 } 91 nextVD, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{}) 92 if err != nil { 93 b.Fatalf("failed to walk to directory %q: %v", name, err) 94 } 95 vd.DecRef(ctx) 96 vd = nextVD 97 filePathBuilder.WriteString(name) 98 filePathBuilder.WriteByte('/') 99 } 100 101 // Create the file that will be stat'd. 102 fd, err := vfsObj.OpenAt(ctx, creds, &vfs.PathOperation{ 103 Root: root, 104 Start: vd, 105 Path: fspath.Parse(filename), 106 FollowFinalSymlink: true, 107 }, &vfs.OpenOptions{ 108 Flags: linux.O_RDWR | linux.O_CREAT | linux.O_EXCL, 109 Mode: 0644, 110 }) 111 vd.DecRef(ctx) 112 if err != nil { 113 b.Fatalf("failed to create file %q: %v", filename, err) 114 } 115 defer fd.DecRef(ctx) 116 filePathBuilder.WriteString(filename) 117 filePath := filePathBuilder.String() 118 119 runtime.GC() 120 b.ResetTimer() 121 for i := 0; i < b.N; i++ { 122 stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{ 123 Root: root, 124 Start: root, 125 Path: fspath.Parse(filePath), 126 FollowFinalSymlink: true, 127 }, &vfs.StatOptions{}) 128 if err != nil { 129 b.Fatalf("stat(%q) failed: %v", filePath, err) 130 } 131 // Sanity check. 132 if stat.Mode&^linux.S_IFMT != 0644 { 133 b.Fatalf("got wrong permissions (%0o)", stat.Mode) 134 } 135 } 136 // Don't include deferred cleanup in benchmark time. 137 b.StopTimer() 138 }) 139 } 140 } 141 142 func BenchmarkTmpfsMountStat(b *testing.B) { 143 for _, depth := range depths { 144 b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { 145 ctx := contexttest.Context(b) 146 creds := auth.CredentialsFromContext(ctx) 147 148 // Create VFS. 149 vfsObj := vfs.VirtualFilesystem{} 150 if err := vfsObj.Init(ctx); err != nil { 151 b.Fatalf("VFS init: %v", err) 152 } 153 vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ 154 AllowUserMount: true, 155 }) 156 mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil) 157 if err != nil { 158 b.Fatalf("failed to create tmpfs root mount: %v", err) 159 } 160 defer mntns.DecRef(ctx) 161 162 var filePathBuilder strings.Builder 163 filePathBuilder.WriteByte('/') 164 165 // Create the mount point. 166 root := mntns.Root(ctx) 167 defer root.DecRef(ctx) 168 pop := vfs.PathOperation{ 169 Root: root, 170 Start: root, 171 Path: fspath.Parse(mountPointName), 172 } 173 if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{ 174 Mode: 0755, 175 }); err != nil { 176 b.Fatalf("failed to create mount point: %v", err) 177 } 178 // Save the mount point for later use. 179 mountPoint, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{}) 180 if err != nil { 181 b.Fatalf("failed to walk to mount point: %v", err) 182 } 183 defer mountPoint.DecRef(ctx) 184 // Create and mount the submount. 185 if _, err := vfsObj.MountAt(ctx, creds, "", &pop, "tmpfs", &vfs.MountOptions{}); err != nil { 186 b.Fatalf("failed to mount tmpfs submount: %v", err) 187 } 188 filePathBuilder.WriteString(mountPointName) 189 filePathBuilder.WriteByte('/') 190 191 // Create nested directories with given depth. 192 vd, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{}) 193 if err != nil { 194 b.Fatalf("failed to walk to mount root: %v", err) 195 } 196 for i := depth; i > 0; i-- { 197 name := fmt.Sprintf("%d", i) 198 pop := vfs.PathOperation{ 199 Root: root, 200 Start: vd, 201 Path: fspath.Parse(name), 202 } 203 if err := vfsObj.MkdirAt(ctx, creds, &pop, &vfs.MkdirOptions{ 204 Mode: 0755, 205 }); err != nil { 206 b.Fatalf("failed to create directory %q: %v", name, err) 207 } 208 nextVD, err := vfsObj.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{}) 209 if err != nil { 210 b.Fatalf("failed to walk to directory %q: %v", name, err) 211 } 212 vd.DecRef(ctx) 213 vd = nextVD 214 filePathBuilder.WriteString(name) 215 filePathBuilder.WriteByte('/') 216 } 217 218 // Create the file that will be stat'd. 219 fd, err := vfsObj.OpenAt(ctx, creds, &vfs.PathOperation{ 220 Root: root, 221 Start: vd, 222 Path: fspath.Parse(filename), 223 FollowFinalSymlink: true, 224 }, &vfs.OpenOptions{ 225 Flags: linux.O_RDWR | linux.O_CREAT | linux.O_EXCL, 226 Mode: 0644, 227 }) 228 vd.DecRef(ctx) 229 if err != nil { 230 b.Fatalf("failed to create file %q: %v", filename, err) 231 } 232 fd.DecRef(ctx) 233 filePathBuilder.WriteString(filename) 234 filePath := filePathBuilder.String() 235 236 runtime.GC() 237 b.ResetTimer() 238 for i := 0; i < b.N; i++ { 239 stat, err := vfsObj.StatAt(ctx, creds, &vfs.PathOperation{ 240 Root: root, 241 Start: root, 242 Path: fspath.Parse(filePath), 243 FollowFinalSymlink: true, 244 }, &vfs.StatOptions{}) 245 if err != nil { 246 b.Fatalf("stat(%q) failed: %v", filePath, err) 247 } 248 // Sanity check. 249 if stat.Mode&^linux.S_IFMT != 0644 { 250 b.Fatalf("got wrong permissions (%0o)", stat.Mode) 251 } 252 } 253 // Don't include deferred cleanup in benchmark time. 254 b.StopTimer() 255 }) 256 } 257 } 258 259 func init() { 260 // Turn off reference leak checking for a benchmarking. 261 refs.SetLeakMode(refs.NoLeakChecking) 262 }