mirror of
https://github.com/hanzoai/go-fuse.git
synced 2026-10-30 14:46:18 +00:00
253 lines
5.2 KiB
Go
253 lines
5.2 KiB
Go
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package benchmark
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"sort"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hanzoai/go-fuse/v2/fs"
|
|
"github.com/hanzoai/go-fuse/v2/fuse"
|
|
"github.com/hanzoai/go-fuse/v2/internal/testutil"
|
|
)
|
|
|
|
func setupFS(node fs.InodeEmbedder, N int, tb testing.TB) string {
|
|
opts := &fs.Options{}
|
|
opts.Debug = testutil.VerboseTest()
|
|
mountPoint := tb.TempDir()
|
|
server, err := fs.Mount(mountPoint, node, opts)
|
|
if err != nil {
|
|
tb.Fatalf("cannot mount %v", err)
|
|
}
|
|
tb.Cleanup(func() {
|
|
err := server.Unmount()
|
|
if err != nil {
|
|
log.Println("error during unmount", err)
|
|
}
|
|
})
|
|
return mountPoint
|
|
}
|
|
|
|
func TestNewStatFs(t *testing.T) {
|
|
fs := &StatFS{}
|
|
for _, n := range []string{
|
|
"file.txt", "sub/dir/foo.txt",
|
|
"sub/dir/bar.txt", "sub/marine.txt"} {
|
|
fs.AddFile(n, fuse.Attr{Mode: syscall.S_IFREG})
|
|
}
|
|
|
|
wd := setupFS(fs, 1, t)
|
|
|
|
names, err := os.ReadDir(wd)
|
|
if err != nil {
|
|
t.Fatalf("failed: %v", err)
|
|
}
|
|
if len(names) != 2 {
|
|
t.Error("readdir /", names)
|
|
}
|
|
|
|
fi, err := os.Lstat(wd + "/sub")
|
|
if err != nil {
|
|
t.Fatalf("failed: %v", err)
|
|
}
|
|
if !fi.IsDir() {
|
|
t.Error("mode", fi)
|
|
}
|
|
names, err = os.ReadDir(wd + "/sub")
|
|
if err != nil {
|
|
t.Fatalf("failed: %v", err)
|
|
}
|
|
if len(names) != 2 {
|
|
t.Error("readdir /sub", names)
|
|
}
|
|
names, err = os.ReadDir(wd + "/sub/dir")
|
|
if err != nil {
|
|
t.Fatalf("failed: %v", err)
|
|
}
|
|
if len(names) != 2 {
|
|
t.Error("readdir /sub/dir", names)
|
|
}
|
|
|
|
fi, err = os.Lstat(wd + "/sub/marine.txt")
|
|
if err != nil {
|
|
t.Fatalf("failed: %v", err)
|
|
}
|
|
if fi.Mode()&os.ModeType != 0 {
|
|
t.Error("mode", fi)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGoFSStat(b *testing.B) {
|
|
b.StopTimer()
|
|
fs := &StatFS{}
|
|
|
|
wd, _ := os.Getwd()
|
|
fileList := wd + "/testpaths.txt"
|
|
files := ReadLines(fileList)
|
|
for _, fn := range files {
|
|
fs.AddFile(fn, fuse.Attr{Mode: syscall.S_IFREG})
|
|
}
|
|
|
|
mnt := setupFS(fs, b.N, b)
|
|
|
|
for i, l := range files {
|
|
files[i] = filepath.Join(mnt, l)
|
|
}
|
|
|
|
threads := runtime.GOMAXPROCS(0)
|
|
if err := TestingBOnePass(b, threads, fileList, mnt); err != nil {
|
|
b.Fatalf("TestingBOnePass %v8", err)
|
|
}
|
|
}
|
|
|
|
func readdir(d string) error {
|
|
f, err := os.Open(d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := f.Readdirnames(-1); err != nil {
|
|
return err
|
|
}
|
|
return f.Close()
|
|
}
|
|
|
|
func BenchmarkGoFSReaddir(b *testing.B) {
|
|
b.StopTimer()
|
|
fs := &StatFS{}
|
|
|
|
wd, _ := os.Getwd()
|
|
dirSet := map[string]struct{}{}
|
|
|
|
for _, fn := range ReadLines(wd + "/testpaths.txt") {
|
|
fs.AddFile(fn, fuse.Attr{Mode: syscall.S_IFREG})
|
|
dirSet[filepath.Dir(fn)] = struct{}{}
|
|
}
|
|
|
|
mnt := setupFS(fs, b.N, b)
|
|
|
|
var dirs []string
|
|
for dir := range dirSet {
|
|
dirs = append(dirs, filepath.Join(mnt, dir))
|
|
}
|
|
b.StartTimer()
|
|
todo := b.N
|
|
for todo > 0 {
|
|
if len(dirs) > todo {
|
|
dirs = dirs[:todo]
|
|
}
|
|
for _, d := range dirs {
|
|
if err := readdir(d); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
todo -= len(dirs)
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func TestingBOnePass(b *testing.B, threads int, filelist, mountPoint string) error {
|
|
runtime.GC()
|
|
var before, after runtime.MemStats
|
|
runtime.ReadMemStats(&before)
|
|
|
|
// We shell out to an external program, so the time spent by
|
|
// the part stat'ing doesn't interfere with the time spent by
|
|
// the FUSE server.
|
|
cmd := exec.Command("./bulkstat.bin",
|
|
fmt.Sprintf("-cpu=%d", threads),
|
|
fmt.Sprintf("-prefix=%s", mountPoint),
|
|
fmt.Sprintf("-N=%d", b.N),
|
|
fmt.Sprintf("-quiet=%v", !testutil.VerboseTest()),
|
|
filelist)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
b.StartTimer()
|
|
err := cmd.Run()
|
|
b.StopTimer()
|
|
runtime.ReadMemStats(&after)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if testutil.VerboseTest() {
|
|
fmt.Printf("GC count %d, total GC time: %d ns/file\n",
|
|
after.NumGC-before.NumGC, (after.PauseTotalNs-before.PauseTotalNs)/uint64(b.N))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BenchmarkLibfuseHighlevelThreadedStat(b *testing.B) {
|
|
b.StopTimer()
|
|
|
|
wd, _ := os.Getwd()
|
|
fileList := wd + "/testpaths.txt"
|
|
lines := ReadLines(fileList)
|
|
unique := map[string]int{}
|
|
for _, l := range lines {
|
|
unique[l] = 1
|
|
dir, _ := filepath.Split(l)
|
|
for dir != "/" && dir != "" {
|
|
unique[dir] = 1
|
|
dir = filepath.Clean(dir)
|
|
dir, _ = filepath.Split(dir)
|
|
}
|
|
}
|
|
|
|
out := []string{}
|
|
for k := range unique {
|
|
out = append(out, k)
|
|
}
|
|
|
|
f, err := os.CreateTemp("", "")
|
|
if err != nil {
|
|
b.Fatalf("failed: %v", err)
|
|
}
|
|
sort.Strings(out)
|
|
for _, k := range out {
|
|
f.Write([]byte(fmt.Sprintf("/%s\n", k)))
|
|
}
|
|
f.Close()
|
|
|
|
mountPoint, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
b.Fatalf("MkdirTemp: %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(wd+"/cstatfs",
|
|
"-o",
|
|
"entry_timeout=0.0,attr_timeout=0.0,ac_attr_timeout=0.0,negative_timeout=0.0",
|
|
mountPoint)
|
|
cmd.Env = append(os.Environ(),
|
|
fmt.Sprintf("STATFS_INPUT=%s", f.Name()))
|
|
cmd.Start()
|
|
|
|
bin, err := exec.LookPath("fusermount")
|
|
if err != nil {
|
|
b.Fatalf("failed: %v", err)
|
|
}
|
|
stop := exec.Command(bin, "-u", mountPoint)
|
|
if err != nil {
|
|
b.Fatalf("failed: %v", err)
|
|
}
|
|
defer stop.Run()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
os.Lstat(mountPoint)
|
|
threads := runtime.GOMAXPROCS(0)
|
|
if err := TestingBOnePass(b, threads, fileList, mountPoint); err != nil {
|
|
b.Fatalf("TestingBOnePass %v", err)
|
|
}
|
|
}
|