Skip to content

vishvish/BenchSSD

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BenchSSD

BenchSSD is a macOS command-line benchmark tool for measuring storage performance on internal and external drives.

The Swift CLI is now the primary benchmarker. It supports sequential throughput tests, sustained write testing, random I/O testing, device inspection, JSON output, and profile-driven workflows intended for external SSD evaluation.

What It Measures

The Swift CLI currently supports:

  • sequential write throughput
  • sequential read throughput
  • sustained sequential write throughput
  • random read IOPS and latency
  • random write IOPS and latency
  • best-effort device and volume inspection

The benchmark is file-based and runs in user space. It does not write to raw devices.

What It Does Not Claim

This tool is designed to be credible and practical, not magical. Results may still be affected by:

  • filesystem cache behavior
  • APFS or ExFAT overhead
  • USB enclosure and cable limits
  • hub bandwidth sharing
  • thermal throttling
  • flash burst-cache exhaustion
  • the limits of user-space benchmarking on macOS

The tool tries to make those limits explicit in its output instead of hiding them.

Build

Build the Swift CLI with:

swift build

Print the current version:

.build/debug/benchssd version

Run help:

.build/debug/benchssd help

Main Commands

Inspect a target volume:

.build/debug/benchssd inspect --path /Volumes/MySSD

Run the benchmark workflow:

.build/debug/benchssd benchmark --path /Volumes/MySSD --profile external-ssd

Run sequential tests only:

.build/debug/benchssd seq --path /Volumes/MySSD --profile quick

Run random I/O tests only:

.build/debug/benchssd rand --path /Volumes/MySSD --profile quick

Emit JSON instead of text:

.build/debug/benchssd benchmark --path /Volumes/MySSD --profile full --json

Benchmark Profiles

The benchmark workflow uses three profiles.

quick

Purpose:

  • fast validation

Behavior:

  • sequential benchmark only
  • no sustained phase
  • no random phase inside benchmark

Best for:

  • sanity checks
  • quick manual verification

external-ssd

Purpose:

  • realistic external SSD testing

Behavior:

  • sequential benchmark
  • sustained write phase
  • no random phase inside benchmark

Best for:

  • checking whether an external SSD falls off after burst cache is exhausted

full

Purpose:

  • deeper characterization

Behavior:

  • sequential benchmark
  • sustained write phase
  • random I/O benchmark included inside benchmark

Best for:

  • more complete storage profiling

Explicit flags such as --test-size-gb, --passes, and --block-size still override the profile defaults.

Benchmark Modes

benchmark

Runs the profile-driven workflow. Depending on profile, this may include:

  • sequential read and write
  • sustained write
  • random read and write

seq

Runs sequential tests only. This is useful when you want faster iteration or to isolate large-block throughput from other workload types.

rand

Runs random I/O tests only. By default it uses 4 KiB and 128 KiB blocks unless --block-size overrides that with a single size.

inspect

Prints mount, filesystem, capacity, and best-effort device metadata for the target path.

Output

Text output includes:

  • target path and mount point
  • filesystem and capacity information
  • chosen profile and configuration
  • per-pass or per-slice results
  • summary statistics
  • warnings and caveats

JSON output exposes the same benchmark facts in a structured form for automation.

Example Output

Text mode:

Benchmark workflow: benchmark
Phase: Phase 11 benchmark profiles

Profile
  Name: quick
  Summary: Fast validation profile with shorter sequential coverage and no sustained or random benchmark phases.

Benchmark command: benchmark
Phase: Phases 3-9 benchmark

Sequential write
  pass 1: 2343.60 MiB/s, 0.437 s, durable

Sequential read
  pass 1: 2138.14 MiB/s, 0.479 s, cold-ish

JSON mode:

{
  "command": "benchmark",
  "phase": "Phase 11 benchmark profiles",
  "profile": {
    "name": "full",
    "randomEnabled": true,
    "sequentialEnabled": true,
    "sustainedEnabled": true
  }
}

Running A Manual Test On An External SSD

Start with inspection:

.build/debug/benchssd inspect --path "/Volumes/MySSD"

For a fast manual run:

.build/debug/benchssd benchmark --path "/Volumes/MySSD" --profile quick --passes 1 --test-size-gb 1

For a more realistic external SSD run:

.build/debug/benchssd benchmark --path "/Volumes/MySSD" --profile external-ssd --passes 1 --test-size-gb 1

If you want random I/O too:

.build/debug/benchssd rand --path "/Volumes/MySSD" --profile quick

Performance And Safety Notes

  • The tool creates temporary files inside the target directory.
  • It does not write to raw block devices.
  • It reserves extra free space before starting.
  • It uses a lock file to prevent overlapping runs on the same target path.
  • Ctrl-C should trigger cleanup, but it is still wise to check for leftover .benchssd-* files after interruption.
  • Sustained mode may stop before its full target duration if the profile safety cap is reached.

Troubleshooting

If a benchmark seems to run for a long time:

  • external-ssd and full can perform a large amount of I/O by default
  • slower USB links, hubs, enclosures, or filesystems can make the run much longer
  • a safety cap may still allow several GiB of writes before the command finishes

If you want a much faster run, use:

.build/debug/benchssd benchmark --path "/Volumes/MySSD" --profile quick --passes 1 --test-size-gb 1

If the tool reports a lock conflict, another benchmark is already using that target path. Wait for it to finish or stop it before starting a second run.

Automation

BenchSSD now uses tag-driven versioning.

Versioning rules:

  • real releases come from Git tags such as v0.2.0
  • development builds derive their version from the latest matching tag, commit count, and short SHA
  • example dev version: 0.2.0+25.gabc1234

The release workflow lives at release.yml.

On pushes to main or master, it:

  • checks out the full Git history
  • generates embedded version metadata from Git
  • runs swift test
  • builds the project

On pushes of tags matching v*, it also:

  • builds a release binary with swift build -c release
  • packages the binary as a zip archive
  • publishes a GitHub Release for that tag

To prepare a release manually:

git tag v0.2.0
git push origin v0.2.0

The CI workflow embeds the tag-derived version into the release binary using generate_version_swift.sh.

About

command-line benchmark tool for measuring storage performance on internal and external drives

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors