Commit 2caa7fd4 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

benchmark: add a test reading from libfuse passthrough_hp

passthrough_hp is the high-performance loopback filesystem. By
exercising it, we can distinguish between kernel bugs (shared between
go-fuse and libfuse) and go-fuse bugs.

Passthrough mode does not work with iflag=direct, because
passthrough_hp does not check if the file is marked "directio",
assuming read() calls in passthrough mode are some kind of error.

Change-Id: I2b6b4c943031e1a2864e15e5bf396aac28c63e31
parent 50cd8164
...@@ -6,85 +6,129 @@ package benchmark ...@@ -6,85 +6,129 @@ package benchmark
import ( import (
"bytes" "bytes"
"flag"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"testing" "testing"
"time"
"github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/internal/testutil" "github.com/hanwen/go-fuse/v2/internal/testutil"
"golang.org/x/sync/errgroup"
) )
func BenchmarkGoFuseMemoryRead(b *testing.B) { func BenchmarkGoFuseMemoryRead(b *testing.B) {
root := &readFS{} root := &readFS{}
benchmarkGoFuseRead(root, b) mnt := setupFS(root, b.N, b)
benchmarkRead(mnt, b, 32, "direct")
} }
const blockSize = 64 * 1024 const blockSize = 64 * 1024
func benchmarkGoFuseRead(root fs.InodeEmbedder, b *testing.B) { func benchmarkRead(mnt string, b *testing.B, readers int, ddflag string) {
wd := setupFS(root, b.N, b) var cmds []*exec.Cmd
for i := 0; i < readers; i++ {
jobs := 32 cmd := exec.Command("dd",
cmds := make([]*exec.Cmd, jobs) fmt.Sprintf("if=%s/foo.txt", mnt),
for i := 0; i < jobs; i++ {
cmds[i] = exec.Command("dd",
fmt.Sprintf("if=%s/foo.txt", wd),
"iflag=direct",
"of=/dev/null", "of=/dev/null",
fmt.Sprintf("bs=%d", blockSize), fmt.Sprintf("bs=%d", blockSize),
fmt.Sprintf("count=%d", b.N)) fmt.Sprintf("count=%d", b.N))
if ddflag != "" {
cmd.Args = append(cmd.Args, "iflag="+ddflag)
}
if testutil.VerboseTest() { if testutil.VerboseTest() {
cmds[i].Stdout = os.Stdout cmd.Stderr = os.Stderr
cmds[i].Stderr = os.Stderr cmd.Stdout = os.Stdout
} else {
buf := &bytes.Buffer{}
cmd.Stderr = buf
cmd.Stdout = buf
} }
cmds = append(cmds, cmd)
} }
b.SetBytes(int64(jobs * blockSize)) b.SetBytes(int64(readers * blockSize))
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
var eg errgroup.Group result := make(chan error, readers)
for i := 0; i < jobs; i++ { for _, cmd := range cmds {
i := i go func(cmd *exec.Cmd) {
eg.Go(func() error { err := cmd.Run()
return cmds[i].Run() if buf, ok := cmd.Stdout.(*bytes.Buffer); ok && err != nil {
}) err = fmt.Errorf("%v: output=%s", err, buf.String())
} }
result <- err
if err := eg.Wait(); err != nil { }(cmd)
b.Fatalf("dd failed: %v", err) }
failures := 0
for range cmds {
if err := <-result; err != nil {
b.Errorf("dd failed: %v", err)
failures++
}
}
if failures > 0 {
b.Errorf("%d out of %d commands", failures, readers)
} }
b.StopTimer() b.StopTimer()
} }
func BenchmarkGoFuseFDRead(b *testing.B) { func BenchmarkGoFuseFDRead(b *testing.B) {
orig := b.TempDir() orig := b.TempDir()
fn := orig + "/foo.txt" fn := orig + "/foo.txt"
f, err := os.Create(fn)
if err != nil { data := bytes.Repeat([]byte{42}, blockSize*b.N)
b.Fatal(err) if err := os.WriteFile(fn, data, 0666); err != nil {
}
defer f.Close()
if err := f.Chmod(0777); err != nil {
b.Fatal(err) b.Fatal(err)
} }
data := bytes.Repeat([]byte{42}, blockSize) root, err := fs.NewLoopbackRoot(orig)
for i := 0; i < b.N; i++ {
_, err := f.Write(data)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
mnt := setupFS(root, b.N, b)
benchmarkRead(mnt, b, 32, "")
}
var libfusePath = flag.String("passthrough_hp", "", "path to libfuse's passthrough_hp")
func BenchmarkLibfuseHP(b *testing.B) {
orig := b.TempDir()
mnt := b.TempDir()
if *libfusePath == "" {
b.Skip("must set --passthrough_hp")
} }
if err := f.Close(); err != nil {
origFN := orig + "/foo.txt"
data := bytes.Repeat([]byte{42}, blockSize*b.N)
if err := os.WriteFile(origFN, data, 0666); err != nil {
b.Fatal(err) b.Fatal(err)
} }
root, err := fs.NewLoopbackRoot(orig) fn := mnt + "/foo.txt"
if err != nil { cmd := exec.Command(*libfusePath, "--foreground")
if testutil.VerboseTest() {
cmd.Args = append(cmd.Args, "--debug", "--debug-fuse")
}
cmd.Args = append(cmd.Args, orig, mnt)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
b.Fatal(err) b.Fatal(err)
} }
b.Cleanup(func() { exec.Command("fusermount", "-u", mnt).Run() })
dt := time.Millisecond
for {
if _, err := os.Stat(fn); err == nil {
break
}
time.Sleep(dt)
dt *= 2
if dt > time.Second {
b.Fatal("file did not appear")
}
}
benchmarkGoFuseRead(root, b) benchmarkRead(mnt, b, 32, "")
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment