dd2026-07-17
Every few weeks somebody posts dd if=/dev/zero of=test bs=1M count=4096 and declares an NVMe drive good or bad. That command measures almost nothing you care about: it's sequential, single-threaded, buffered through the page cache, zeros compress on some devices, and it reports one number (bandwidth) when the interesting ones are p99 latency and IOPS under contention.
fio — the Flexible I/O Tester — is what Jens Axboe wrote because he was tired of it. Yes, that Jens Axboe: the guy who maintains the Linux block layer and wrote io_uring. His benchmark tool has been the industry standard for filesystem, block, and SAN qualification for fifteen years and most application developers have never heard of it.
The killer feature is the ioengine abstraction. fio doesn't just call read() in a loop — it can drive real workloads through libaio, io_uring, posixaio, mmap, splice, SG_IO to raw SCSI, or a network target:
# The lie: dd says my NVMe does 3.2 GB/s. What can it actually do?
fio --name=nvme-truth \
--ioengine=io_uring --direct=1 --iodepth=64 --numjobs=4 \
--rw=randread --bs=4k --size=4G --runtime=60 \
--time_based --group_reporting --randrepeat=0
You get back IOPS, bandwidth, and full latency histograms — p50/p95/p99/p99.9/p99.99 — for both submission and completion. --direct=1 bypasses the page cache so you're measuring the device, not RAM. --randrepeat=0 re-seeds the RNG so a smart controller can't cache your access pattern.
The real strength shows up in job files. Mixed workloads that mimic actual databases:
# pg-like.fio — 70/30 read/write, 16k blocks, fsync every 32 writes
[global]
ioengine=io_uring
direct=1
runtime=300
time_based
group_reporting
[reader]
rw=randread
bs=16k
iodepth=32
numjobs=8
rate_iops=20000
[writer]
rw=randwrite
bs=16k
iodepth=8
numjobs=2
fsync=32
Run it: fio pg-like.fio. Change rate_iops to cap traffic and see how latency degrades under a known load — the shape of the latency curve as you approach saturation is what actually matters for capacity planning, not the peak number.
A few things fio does that nobody else in this space does well:
fio --server on twenty machines, fio --client=hosts.txt job.fio from one — coordinated, aggregated output. This is how you benchmark a Ceph cluster.verify=crc32c writes known patterns and reads them back. Silent corruption on cheap SSDs? fio will find it.read_iolog= takes a blktrace capture from production and replays the exact I/O pattern against your test rig.log_hist_msec=1000 emits per-second histograms you can feed straight to hdrhistogram or ttyplot.Compared to alternatives: hdparm -tT measures the cache, bonnie++ has a fixed workload from 1996, iozone works but its output looks like a fax from 1998, and ioping (covered previously) is the right tool for latency probing — not throughput characterization. fio is the one you reach for when someone hands you a mystery LUN and asks "is this fast enough?"
Install: it's just apt install fio or dnf install fio. No dependencies worth mentioning. The manpage is genuinely one of the best-written in the Unix world — read HOWTO.rst in the source tree if you want an education in what modern block I/O actually looks like.
dd answers a question you didn't ask; fio answers the ones you actually need — IOPS, latency percentiles, and behavior under realistic parallel workloads, straight from the man who maintains Linux's block layer.
