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
Assisted-by: GLM 5.3 Flash
169 lines
4.0 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|