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  }