# DriveForge (CLI prototype) This working prototype is a multi-target sequential write/read throughput comparison tool: it runs the same proven benchmark technique as the sibling DiskOps tool (write a temp file in fixed-size chunks, fsync it to force data to stable storage, time the write, then read it back sequentially and time that too) independently across several directories/mount points given on one command line, and prints a ranked comparison report showing which target is fastest and slowest for write and for read. Each target is fault-isolated - a directory that doesn't exist or isn't writable is reported as FAILED with a reason, and the rest of the comparison still completes. Be clear-eyed about what this is: it measures file-API throughput through the OS and filesystem against each target directory, NOT raw block-device performance. The full DriveForge product concept also covers real partition-table manipulation, cloning, migration, bad-sector awareness, and SMART data - those all need raw-device access and OS-privileged operations that cannot be implemented portably with only the Go standard library (no external deps, no OS-specific syscalls/build tags), so they are on the roadmap rather than in this prototype. See ../plan.md for the full product plan. ## Build from source Requires Go 1.24+, no external dependencies. go build -o driveforge . Cross-compile for another platform: GOOS=windows GOARCH=amd64 go build -o driveforge.exe . GOOS=darwin GOARCH=arm64 go build -o driveforge . ## Usage driveforge bench [ ...] [--size 256MB] [--block 1MB] [--json] Runs the write-then-read benchmark against a temp file created at /.driveforge-benchmark-tmp inside EACH target directory given, independently, using the exact same --size and --block for every target in the invocation so the comparison is apples-to-apples. The temp file in each target is always deleted afterward, including on error paths. After all targets complete, a table ranked by write MB/s descending (fastest write first - the more actionable signal for "where should a write-heavy workload go") is printed, with write MB/s, read MB/s, and the shared size/block settings shown per target. Flags: --size Total bytes to write/read per target, e.g. 256MB, 1GiB, 512KB (default 256MB). Accepts SI suffixes (KB/MB/GB, powers of 1000) and binary suffixes (KiB/MiB/GiB, powers of 1024), case-insensitive. A bare number is treated as a byte count. The same value is used for every target in the invocation. --block Chunk size for each write/read call, e.g. 1MB, 256KB (default 1MB). Same suffix rules as --size. Does not need to divide --size evenly - the last chunk is simply shorter. The same value is used for every target in the invocation. --json Emit a machine-readable JSON report instead of human-readable text, including size_bytes/block_bytes (shared across targets), a per-target breakdown with ok/error/throughput fields, and a ranked_dirs list in comparison order. Fault isolation: A target that doesn't exist, isn't a directory, or isn't writable is recorded as a failed target with a human-readable reason and excluded from the write-MB/s ranking - it does NOT stop the other targets from being benchmarked. The command exits non-zero only if EVERY target failed; if at least one target succeeded, it exits 0 even when other targets in the same run failed. How the benchmark works (same technique per target as DiskOps): - One random block of size --block is pre-generated with crypto/rand and reused for every chunk written to that target. Genuinely random (not repeating zero/pattern) data is used so filesystem-level compression or dedup can't skew the numbers, but generating fresh random data for every chunk with crypto/rand is slow enough to dominate the timed write loop and artificially cap the reported throughput - so the random-generation cost is paid once per target, up front, outside the timer, and the same block is reused. - The write phase is timed from the first byte written through a final f.Sync() call. Without the fsync, the benchmark would only measure how fast data lands in the OS page cache, not how fast it reaches stable storage, so the fsync is deliberately included in the timed write. - The read phase closes and reopens the file, then reads it back sequentially in --block-sized chunks into a reused buffer (the data itself is discarded, not compared). Bytes read are checked against bytes written; a mismatch is reported as a failure for that target. Caveats: - This is sequential, single-threaded, file-API throughput only per target - it does not reflect random I/O, queue depth, or raw block-device performance. - OS and filesystem caching mean small --size values can report unrealistically high numbers (especially on the read side, where a small file may be served entirely from cache). This effect is stronger when multiple targets in one invocation share the same underlying filesystem or page cache (e.g. two directories on the same disk) - the comparison is still internally consistent (same size/block for all targets) but may not reflect real differences between physically distinct drives. Use --size of at least a few hundred MB for a meaningful result across genuinely different storage devices. - Targets are benchmarked sequentially, one after another, not in parallel - total run time scales with the number of targets given. Example: $ driveforge bench /mnt/ssd /mnt/hdd --size 512MB DriveForge multi-target benchmark comparison targets: 2 total size: 488.28 MiB (512000000 bytes) per target block size: 976.56 KiB (1000000 bytes) per target Rank Write MB/s Read MB/s Target ---- ---------- ---------- ------ 1 610.42 1120.88 /mnt/ssd 2 142.07 198.35 /mnt/hdd Ranked by write MB/s descending (fastest write first). Note: sequential, single-threaded, file-API throughput against each target filesystem - not a raw block-device benchmark. The same --size/--block was used for every target for an apples-to-apples comparison. Small --size values can be inflated by OS/filesystem caching, especially when targets share the same underlying filesystem or page cache. ## Prebuilt binaries See ../downloads/ for prebuilt binaries (Windows/macOS/Linux) and CHECKSUMS.txt for their SHA-256 hashes. These are unsigned indie builds - Windows SmartScreen and macOS Gatekeeper will warn on first run, which is expected until a code-signing certificate is in place.