|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +**dperf** is a MinIO drive performance measurement tool that identifies slow drives by performing parallel I/O operations on multiple file paths. It measures read/write throughput and displays results sorted by performance (fastest drives first). |
| 8 | + |
| 9 | +## Build & Test Commands |
| 10 | + |
| 11 | +### Build |
| 12 | +```bash |
| 13 | +make build # Builds dperf binary with CGO_ENABLED=0 |
| 14 | +``` |
| 15 | + |
| 16 | +### Install |
| 17 | +```bash |
| 18 | +make install # Builds and installs to $GOPATH/bin/dperf |
| 19 | +go install github.com/minio/dperf@latest # Install from source |
| 20 | +``` |
| 21 | + |
| 22 | +### Run |
| 23 | +```bash |
| 24 | +./dperf /mnt/drive1 # Single drive |
| 25 | +./dperf /mnt/drive{1..6} # Multiple drives (parallel) |
| 26 | +./dperf --serial /mnt/drive{1..6} # Multiple drives (sequential) |
| 27 | +./dperf --write-only /mnt/drive1 # Write-only benchmark |
| 28 | +./dperf -v /mnt/drive{1..6} # Verbose output (per-drive stats) |
| 29 | +``` |
| 30 | + |
| 31 | +### Key Flags |
| 32 | +- `-b, --blocksize`: Read/write block size (default: "4MiB") |
| 33 | +- `-f, --filesize`: Amount of data per drive (default: "1GiB") |
| 34 | +- `-i, --ioperdrive`: Concurrent I/O per drive (default: 4) |
| 35 | +- `--serial`: Run tests sequentially instead of parallel |
| 36 | +- `--write-only`: Run write-only tests |
| 37 | +- `-v, --verbose`: Show individual path stats (default shows only aggregate) |
| 38 | + |
| 39 | +### Profiling (Hidden Flags) |
| 40 | +```bash |
| 41 | +./dperf --prof.cpu --prof.dir=./profiles /mnt/drive1 # CPU profiling |
| 42 | +./dperf --prof.mem --prof.dir=./profiles /mnt/drive1 # Memory profiling |
| 43 | +./dperf --prof.cpuio --prof.dir=./profiles /mnt/drive1 # CPU/IO profiling |
| 44 | +``` |
| 45 | + |
| 46 | +Other profile types: `--prof.block`, `--prof.mutex`, `--prof.trace`, `--prof.thread` |
| 47 | + |
| 48 | +### Clean |
| 49 | +```bash |
| 50 | +make clean # Remove *.test and temporary files |
| 51 | +``` |
| 52 | + |
| 53 | +## Architecture |
| 54 | + |
| 55 | +### Package Structure |
| 56 | + |
| 57 | +#### `main.go` |
| 58 | +Entry point that sets up signal handling (SIGINT, SIGTERM, SIGSEGV) and calls into the `cmd` package. |
| 59 | + |
| 60 | +#### `cmd/cmd.go` |
| 61 | +- Defines the Cobra command structure and all CLI flags |
| 62 | +- Validates input parameters (blocksize/filesize must be ≥4K and multiples of 4K) |
| 63 | +- Validates paths (must be directories, not root, must exist) |
| 64 | +- Orchestrates profiling setup via `startTraces()` |
| 65 | +- Creates `DrivePerf` struct and calls `RunAndRender()` |
| 66 | + |
| 67 | +#### `pkg/dperf/perf.go` |
| 68 | +Core performance testing logic: |
| 69 | +- `DrivePerf`: Main configuration struct with Serial, BlockSize, FileSize, IOPerDrive, WriteOnly, Verbose options |
| 70 | +- `Run()`: Executes tests either serially or in parallel (goroutines per path) |
| 71 | +- `runTests()`: Per-path orchestration - launches IOPerDrive goroutines for write, then read |
| 72 | +- `RunAndRender()`: Runs tests and displays sorted results |
| 73 | + |
| 74 | +#### `pkg/dperf/run_linux.go` (Linux only) |
| 75 | +Platform-specific I/O implementation using direct I/O (O_DIRECT): |
| 76 | +- `runWriteTest()`: Opens file with O_DIRECT|O_RDWR|O_CREATE, writes FileSize bytes using random data, measures throughput |
| 77 | +- `runReadTest()`: Opens file with O_DIRECT|O_RDONLY, reads FileSize bytes, measures throughput |
| 78 | +- `copyAligned()`: Core I/O function handling aligned/unaligned buffers for direct I/O |
| 79 | +- Uses `syscall.Fdatasync()` for write durability |
| 80 | +- Uses `unix.Fadvise(FADV_SEQUENTIAL)` for read optimization |
| 81 | +- Random data generation via `github.com/minio/pkg/v3/rng` |
| 82 | + |
| 83 | +#### `pkg/dperf/run_other.go` (Non-Linux) |
| 84 | +Stub implementation returning `ErrNotImplemented` - dperf only works on Linux. |
| 85 | + |
| 86 | +#### `pkg/dperf/result.go` |
| 87 | +Output formatting: |
| 88 | +- `DrivePerfResult`: Contains Path, WriteThroughput, ReadThroughput, Error |
| 89 | +- `render()`: Displays results in colored tables using `github.com/minio/pkg/v3/console` |
| 90 | +- Shows per-drive stats in verbose mode, always shows aggregate TotalWRITE/TotalREAD |
| 91 | + |
| 92 | +### Key Technical Details |
| 93 | + |
| 94 | +**Direct I/O Requirements:** |
| 95 | +- Block size must be ≥4096 bytes and a multiple of 4096 (O_DIRECT alignment requirement) |
| 96 | +- File size must be ≥4096 bytes and a multiple of 4096 |
| 97 | +- Buffers allocated via `directio.AlignedBlock()` for page alignment |
| 98 | +- When unaligned writes occur, O_DIRECT is disabled and fdatasync is used |
| 99 | + |
| 100 | +**Concurrency Model:** |
| 101 | +- By default, runs all paths in parallel (goroutine per path) |
| 102 | +- Each path spawns IOPerDrive goroutines (default: 4) for concurrent I/O |
| 103 | +- `--serial` flag forces sequential path execution |
| 104 | +- Write tests complete before read tests begin (per path) |
| 105 | + |
| 106 | +**Test Files:** |
| 107 | +- Created at `{path}/{uuid}/.writable-check.tmp-{0..IOPerDrive-1}` |
| 108 | +- Automatically cleaned up after test via `defer os.RemoveAll()` |
| 109 | + |
| 110 | +**Result Sorting:** |
| 111 | +- Results sorted by ReadThroughput descending (fastest first) |
| 112 | +- Helps identify slowest drives quickly |
| 113 | + |
| 114 | +## Kubernetes Deployment |
| 115 | + |
| 116 | +See `dperf.yaml` for example Job that benchmarks PersistentVolumeClaims (useful for DirectPV testing). |
| 117 | + |
| 118 | +## Requirements |
| 119 | + |
| 120 | +- Linux OS (uses O_DIRECT, unix.Fadvise, syscall.Fdatasync) |
| 121 | +- Go 1.17+ for building |
| 122 | +- Write permissions on target paths |
| 123 | +- Block devices supporting direct I/O |
0 commit comments