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:
parent
7bfad8ebb6
commit
690308111c
|
@ -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",
|
||||
],
|
||||
)
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue