ext: Benchmark tests.

Added benchmark tests which emulate memfs benchmarks.

Stat benchmarks
BenchmarkVFS2Ext4fsStat/1-12      	10000000	       145 ns/op
BenchmarkVFS2Ext4fsStat/2-12      	10000000	       170 ns/op
BenchmarkVFS2Ext4fsStat/3-12      	10000000	       202 ns/op
BenchmarkVFS2Ext4fsStat/8-12      	 3000000	       374 ns/op
BenchmarkVFS2Ext4fsStat/64-12     	  500000	      2159 ns/op
BenchmarkVFS2Ext4fsStat/100-12    	  300000	      3459 ns/op

BenchmarkVFS1TmpfsStat/1-12       	 5000000	       348 ns/op
BenchmarkVFS1TmpfsStat/2-12       	 3000000	       487 ns/op
BenchmarkVFS1TmpfsStat/3-12       	 2000000	       655 ns/op
BenchmarkVFS1TmpfsStat/8-12       	 1000000	      1365 ns/op
BenchmarkVFS1TmpfsStat/64-12      	  200000	      9565 ns/op
BenchmarkVFS1TmpfsStat/100-12     	  100000	     15158 ns/op

BenchmarkVFS2MemfsStat/1-12       	10000000	       133 ns/op
BenchmarkVFS2MemfsStat/2-12       	10000000	       155 ns/op
BenchmarkVFS2MemfsStat/3-12       	10000000	       182 ns/op
BenchmarkVFS2MemfsStat/8-12       	 5000000	       310 ns/op
BenchmarkVFS2MemfsStat/64-12      	 1000000	      1659 ns/op
BenchmarkVFS2MemfsStat/100-12     	  500000	      2787 ns/op

Mount Stat benchmarks
BenchmarkVFS2ExtfsMountStat/1-12  	 5000000	       245 ns/op
BenchmarkVFS2ExtfsMountStat/2-12  	 5000000	       266 ns/op
BenchmarkVFS2ExtfsMountStat/3-12  	 5000000	       304 ns/op
BenchmarkVFS2ExtfsMountStat/8-12  	 3000000	       456 ns/op
BenchmarkVFS2ExtfsMountStat/64-12 	  500000	      2308 ns/op
BenchmarkVFS2ExtfsMountStat/100-12   300000	      3482 ns/op

BenchmarkVFS1TmpfsMountStat/1-12  	 3000000	       488 ns/op
BenchmarkVFS1TmpfsMountStat/2-12  	 2000000	       658 ns/op
BenchmarkVFS1TmpfsMountStat/3-12  	 2000000	       806 ns/op
BenchmarkVFS1TmpfsMountStat/8-12  	 1000000	      1514 ns/op
BenchmarkVFS1TmpfsMountStat/64-12 	  100000	     10037 ns/op
BenchmarkVFS1TmpfsMountStat/100-12        100000	     15280 ns/op

BenchmarkVFS2MemfsMountStat/1-12           	10000000	       212 ns/op
BenchmarkVFS2MemfsMountStat/2-12           	 5000000	       232 ns/op
BenchmarkVFS2MemfsMountStat/3-12           	 5000000	       264 ns/op
BenchmarkVFS2MemfsMountStat/8-12           	 3000000	       390 ns/op
BenchmarkVFS2MemfsMountStat/64-12          	 1000000	      1813 ns/op
BenchmarkVFS2MemfsMountStat/100-12         	  500000	      2812 ns/op

PiperOrigin-RevId: 262477158
This commit is contained in:
Ayush Ranjan 2019-08-08 18:44:28 -07:00 committed by gVisor bot
parent 7bfad8ebb6
commit 690308111c
5 changed files with 287 additions and 6 deletions

View File

@ -0,0 +1,16 @@
load("//tools/go_stateify:defs.bzl", "go_test")
package(licenses = ["notice"])
go_test(
name = "benchmark_test",
size = "small",
srcs = ["benchmark_test.go"],
deps = [
"//pkg/sentry/context",
"//pkg/sentry/context/contexttest",
"//pkg/sentry/fs/ext",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/vfs",
],
)

View File

@ -0,0 +1,193 @@
// Copyright 2019 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// These benchmarks emulate memfs benchmarks. Ext4 images must be created
// before this benchmark is run using the `make_deep_ext4.sh` script at
// /tmp/image-{depth}.ext4 for all the depths tested below.
package benchmark_test
import (
"fmt"
"os"
"runtime"
"strings"
"testing"
"gvisor.dev/gvisor/pkg/sentry/context"
"gvisor.dev/gvisor/pkg/sentry/context/contexttest"
"gvisor.dev/gvisor/pkg/sentry/fs/ext"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
)
var depths = []int{1, 2, 3, 8, 64, 100}
const filename = "file.txt"
// setUp opens imagePath as an ext Filesystem and returns all necessary
// elements required to run tests. If error is nil, it also returns a tear
// down function which must be called after the test is run for clean up.
func setUp(b *testing.B, imagePath string) (context.Context, *vfs.VirtualFilesystem, *vfs.VirtualDentry, func(), error) {
f, err := os.Open(imagePath)
if err != nil {
return nil, nil, nil, nil, err
}
ctx := contexttest.Context(b)
creds := auth.CredentialsFromContext(ctx)
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("extfs", ext.FilesystemType{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, imagePath, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())})
if err != nil {
f.Close()
return nil, nil, nil, nil, err
}
root := mntns.Root()
tearDown := func() {
root.DecRef()
if err := f.Close(); err != nil {
b.Fatalf("tearDown failed: %v", err)
}
}
return ctx, vfsObj, &root, tearDown, nil
}
// mount mounts extfs at the path operation passed. Returns a tear down
// function which must be called after the test is run for clean up.
func mount(b *testing.B, imagePath string, vfsfs *vfs.VirtualFilesystem, pop *vfs.PathOperation) func() {
b.Helper()
f, err := os.Open(imagePath)
if err != nil {
b.Fatalf("could not open image at %s: %v", imagePath, err)
}
ctx := contexttest.Context(b)
creds := auth.CredentialsFromContext(ctx)
if err := vfsfs.NewMount(ctx, creds, imagePath, pop, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())}); err != nil {
b.Fatalf("failed to mount tmpfs submount: %v", err)
}
return func() {
if err := f.Close(); err != nil {
b.Fatalf("tearDown failed: %v", err)
}
}
}
// BenchmarkVFS2Ext4fsStat emulates BenchmarkVFS2MemfsStat.
func BenchmarkVFS2Ext4fsStat(b *testing.B) {
for _, depth := range depths {
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", depth))
if err != nil {
b.Fatalf("setUp failed: %v", err)
}
defer tearDown()
creds := auth.CredentialsFromContext(ctx)
var filePathBuilder strings.Builder
filePathBuilder.WriteByte('/')
for i := 1; i <= depth; i++ {
filePathBuilder.WriteString(fmt.Sprintf("%d", i))
filePathBuilder.WriteByte('/')
}
filePathBuilder.WriteString(filename)
filePath := filePathBuilder.String()
runtime.GC()
b.ResetTimer()
for i := 0; i < b.N; i++ {
stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
Root: *root,
Start: *root,
Pathname: filePath,
FollowFinalSymlink: true,
}, &vfs.StatOptions{})
if err != nil {
b.Fatalf("stat(%q) failed: %v", filePath, err)
}
// Sanity check.
if stat.Size > 0 {
b.Fatalf("got wrong file size (%d)", stat.Size)
}
}
})
}
}
// BenchmarkVFS2ExtfsMountStat emulates BenchmarkVFS2MemfsMountStat.
func BenchmarkVFS2ExtfsMountStat(b *testing.B) {
for _, depth := range depths {
b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) {
// Create root extfs with depth 1 so we can mount extfs again at /1/.
ctx, vfsfs, root, tearDown, err := setUp(b, fmt.Sprintf("/tmp/image-%d.ext4", 1))
if err != nil {
b.Fatalf("setUp failed: %v", err)
}
defer tearDown()
creds := auth.CredentialsFromContext(ctx)
mountPointName := "/1/"
pop := vfs.PathOperation{
Root: *root,
Start: *root,
Pathname: mountPointName,
}
// Save the mount point for later use.
mountPoint, err := vfsfs.GetDentryAt(ctx, creds, &pop, &vfs.GetDentryOptions{})
if err != nil {
b.Fatalf("failed to walk to mount point: %v", err)
}
defer mountPoint.DecRef()
// Create extfs submount.
mountTearDown := mount(b, fmt.Sprintf("/tmp/image-%d.ext4", depth), vfsfs, &pop)
defer mountTearDown()
var filePathBuilder strings.Builder
filePathBuilder.WriteString(mountPointName)
for i := 1; i <= depth; i++ {
filePathBuilder.WriteString(fmt.Sprintf("%d", i))
filePathBuilder.WriteByte('/')
}
filePathBuilder.WriteString(filename)
filePath := filePathBuilder.String()
runtime.GC()
b.ResetTimer()
for i := 0; i < b.N; i++ {
stat, err := vfsfs.StatAt(ctx, creds, &vfs.PathOperation{
Root: *root,
Start: *root,
Pathname: filePath,
FollowFinalSymlink: true,
}, &vfs.StatOptions{})
if err != nil {
b.Fatalf("stat(%q) failed: %v", filePath, err)
}
// Sanity check. touch(1) always creates files of size 0 (empty).
if stat.Size > 0 {
b.Fatalf("got wrong file size (%d)", stat.Size)
}
}
})
}
}

View File

@ -0,0 +1,72 @@
#!/bin/bash
# Copyright 2019 The gVisor Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script creates an ext4 image with $1 depth of directories and a file in
# the inner most directory. The created file is at path /1/2/.../depth/file.txt.
# The ext4 image is written to $2. The image is temporarily mounted at
# /tmp/mountpoint. This script must be run with sudo privileges.
# Usage:
# sudo bash make_deep_ext4.sh {depth} {output path}
# Check positional arguments.
if [ "$#" -ne 2 ]; then
echo "Usage: sudo bash make_deep_ext4.sh {depth} {output path}"
exit 1
fi
# Make sure depth is a non-negative number.
if ! [[ "$1" =~ ^[0-9]+$ ]]; then
echo "Depth must be a non-negative number."
exit 1
fi
# Create a 1 MB filesystem image at the requested output path.
rm -f $2
fallocate -l 1M $2
if [ $? -ne 0 ]; then
echo "fallocate failed"
exit $?
fi
# Convert that blank into an ext4 image.
mkfs.ext4 -j $2
if [ $? -ne 0 ]; then
echo "mkfs.ext4 failed"
exit $?
fi
# Mount the image.
MOUNTPOINT=/tmp/mountpoint
mkdir -p $MOUNTPOINT
mount -o loop $2 $MOUNTPOINT
if [ $? -ne 0 ]; then
echo "mount failed"
exit $?
fi
# Create nested directories and the file.
if [ "$1" -eq 0 ]; then
FILEPATH=$MOUNTPOINT/file.txt
else
FILEPATH=$MOUNTPOINT/$(seq -s '/' 1 $1)/file.txt
fi
mkdir -p $(dirname $FILEPATH) || exit
touch $FILEPATH
# Clean up.
umount $MOUNTPOINT
rm -rf $MOUNTPOINT

View File

@ -30,11 +30,11 @@ import (
"gvisor.dev/gvisor/pkg/syserror"
)
// filesystemType implements vfs.FilesystemType.
type filesystemType struct{}
// FilesystemType implements vfs.FilesystemType.
type FilesystemType struct{}
// Compiles only if filesystemType implements vfs.FilesystemType.
var _ vfs.FilesystemType = (*filesystemType)(nil)
// Compiles only if FilesystemType implements vfs.FilesystemType.
var _ vfs.FilesystemType = (*FilesystemType)(nil)
// getDeviceFd returns an io.ReaderAt to the underlying device.
// Currently there are two ways of mounting an ext(2/3/4) fs:
@ -92,7 +92,7 @@ func isCompatible(sb disklayout.SuperBlock) bool {
}
// NewFilesystem implements vfs.FilesystemType.NewFilesystem.
func (fstype filesystemType) NewFilesystem(ctx context.Context, creds *auth.Credentials, source string, opts vfs.NewFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
func (FilesystemType) NewFilesystem(ctx context.Context, creds *auth.Credentials, source string, opts vfs.NewFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
// TODO(b/134676337): Ensure that the user is mounting readonly. If not,
// EACCESS should be returned according to mount(2). Filesystem independent
// flags (like readonly) are currently not available in pkg/sentry/vfs.

View File

@ -65,7 +65,7 @@ func setUp(t *testing.T, imagePath string) (context.Context, *vfs.VirtualFilesys
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("extfs", filesystemType{})
vfsObj.MustRegisterFilesystemType("extfs", FilesystemType{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, localImagePath, "extfs", &vfs.NewFilesystemOptions{InternalData: int(f.Fd())})
if err != nil {
f.Close()