Files
nfs/internal/nfsfs/local_bench_test.go
T
petrbalvin a9b8039ef7
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

169 lines
4.0 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package nfsfs
import (
"fmt"
"os"
"path/filepath"
"testing"
)
const benchChunk = 64 << 10
// benchRoot builds a backend over a fresh directory with one file of the
// given size, filled with a repeating pattern, and returns the backend
// and the file handle. Setup runs once, outside the measured region.
func benchRoot(b *testing.B, size int) (*Local, Handle) {
b.Helper()
root := b.TempDir()
l, err := NewLocal(root)
if err != nil {
b.Fatalf("NewLocal: %v", err)
}
p := filepath.Join(root, "file")
buf := make([]byte, 1<<20)
for i := range buf {
buf[i] = byte(i)
}
f, err := os.Create(p)
if err != nil {
b.Fatalf("Create: %v", err)
}
for written := 0; written < size; written += len(buf) {
if _, err := f.Write(buf); err != nil {
b.Fatalf("Write: %v", err)
}
}
if err := f.Close(); err != nil {
b.Fatalf("Close: %v", err)
}
rh, err := l.Root()
if err != nil {
b.Fatalf("Root: %v", err)
}
h, _, err := l.Lookup(rh, "file")
if err != nil {
b.Fatalf("Lookup: %v", err)
}
return l, h
}
// BenchmarkRead64K reads 64 KiB at a time from a 64 MiB file, cycling
// through the offsets so every read touches pages the previous read left.
func BenchmarkRead64K(b *testing.B) {
l, h := benchRoot(b, 64<<20)
off := int64(0)
b.SetBytes(benchChunk)
b.ResetTimer()
for b.Loop() {
if _, err := l.Read(h, off, benchChunk); err != nil {
b.Fatalf("Read: %v", err)
}
off += benchChunk
if off > 64<<20-benchChunk {
off = 0
}
}
}
// BenchmarkWrite64K writes 64 KiB at a time over a preallocated 64 MiB
// file, cycling through the offsets, so no read has to grow the file.
func BenchmarkWrite64K(b *testing.B) {
l, h := benchRoot(b, 64<<20)
buf := make([]byte, benchChunk)
off := int64(0)
b.SetBytes(benchChunk)
b.ResetTimer()
for b.Loop() {
if _, err := l.Write(h, off, buf); err != nil {
b.Fatalf("Write: %v", err)
}
off += benchChunk
if off > 64<<20-benchChunk {
off = 0
}
}
}
// BenchmarkGetattr reports the attributes of one file.
func BenchmarkGetattr(b *testing.B) {
l, h := benchRoot(b, 1<<20)
b.ResetTimer()
for b.Loop() {
if _, err := l.Getattr(h); err != nil {
b.Fatalf("Getattr: %v", err)
}
}
}
// BenchmarkLookup resolves one name under the export root of a directory
// holding a hundred files.
func BenchmarkLookup(b *testing.B) {
root := b.TempDir()
l, err := NewLocal(root)
if err != nil {
b.Fatalf("NewLocal: %v", err)
}
rh, err := l.Root()
if err != nil {
b.Fatalf("Root: %v", err)
}
for i := range 100 {
name := fmt.Sprintf("f%d", i)
if err := os.WriteFile(filepath.Join(root, name), []byte("x"), 0o644); err != nil {
b.Fatalf("WriteFile: %v", err)
}
}
b.ResetTimer()
i := 0
for b.Loop() {
if _, _, err := l.Lookup(rh, fmt.Sprintf("f%d", i%100)); err != nil {
b.Fatalf("Lookup: %v", err)
}
i++
}
}
// BenchmarkReadDirPage64 pages a 10 000 entry directory 64 entries at a
// time after the first page: with the listing cache the cost of a page
// is the page, and the benchmark holds that to the measurement.
func BenchmarkReadDirPage64(b *testing.B) {
root := b.TempDir()
l, err := NewLocal(root)
if err != nil {
b.Fatalf("NewLocal: %v", err)
}
dir := filepath.Join(root, "big")
if err := os.Mkdir(dir, 0o755); err != nil {
b.Fatalf("Mkdir: %v", err)
}
for i := range 10000 {
if err := os.WriteFile(filepath.Join(dir, fmt.Sprintf("f%04d", i)), []byte("x"), 0o644); err != nil {
b.Fatalf("WriteFile: %v", err)
}
}
rh, err := l.Root()
if err != nil {
b.Fatalf("Root: %v", err)
}
h, _, err := l.Lookup(rh, "big")
if err != nil {
b.Fatalf("Lookup: %v", err)
}
if _, err := l.ReadDir(h, 0, 64); err != nil {
b.Fatalf("first page: %v", err)
}
b.ResetTimer()
for b.Loop() {
page, err := l.ReadDir(h, 0, 64)
if err != nil {
b.Fatalf("ReadDir: %v", err)
}
if len(page.Entries) != 64 || page.EOF {
b.Fatalf("page: %d entries, eof %v", len(page.Entries), page.EOF)
}
}
}