Fio
Fio
fio 是一个通用 I/O 压测工具,全名 Flexible I/O Tester。它常用来测试 SSD、NVMe、文件系统、块设备的吞吐、IOPS、延迟、队列深度行为等。对你写 SSD 固件来说,fio 很适合验证 FTL、GC、wearleveling、write cliff、读写延迟抖动这些问题。
安装
sudo apt-get install fio
fio --version
概念
Job
fio的测试任务被组织为一个或多个"作业"(Job)。每个作业定义了一组特定的I/O参数和行为。一个作业文件中可以包含多个作业定义。
I/O引擎(I/O Engine)
fio支持多种I/O引擎,用于实现不同的I/O操作方式。常见的I/O引擎包括:
libaio:Linux异步I/O
sync:同步I/O
mmap:内存映射I/O
psync:POSIX同步I/O
io_uring:Linux新的异步I/O接口(需要内核4.18+)
块大小(Block Size)
指每次I/O操作的数据大小,通常以bs参数指定,可以是字节(B)、千字节(K)、兆字节(M)等单位。
I/O模式(I/O Pattern)
fio支持多种I/O模式,主要包括:
read:顺序读write:顺序写randread:随机读randwrite:随机写randrw:混合随机读写
队列深度(I/O Depth)
指在等待前一批I/O完成之前,可以提交的最大I/O请求数。对于异步I/O引擎(如libaio),队列深度是一个重要的性能调优参数。
核心概念
直接I/O(Direct I/O)
通过设置direct=1参数,fio可以绕过操作系统的文件缓存,直接与存储设备进行I/O操作,这对于评估底层存储设备的真实性能非常有用
命令参数
fio 的测试由几个参数决定:
| 参数 | 含义 | 具体样例 |
|---|---|---|
--name |
测试任务名称,便于识别结果 | --name=seq-read |
--filename |
测试目标,可以是普通文件或块设备 | --filename=/mnt/testfile 或 --filename=/dev/nvme0n1 |
--rw |
访问模式:read 顺序读、write 顺序写、randread 随机读、randwrite 随机写、randrw 随机混合读写 |
--rw=randread如果是 --rw=randrw需要指定比例 --rwmixread=70 |
--bs |
每次 I/O 的块大小 | --bs=4k、--bs=128k |
--iodepth |
I/O 队列深度;常用值有 1、4、32、64 |
--iodepth=32 |
--numjobs |
并发 job 数量 | --numjobs=4 |
--size |
每个 job 使用的测试数据范围 | --size=10G |
--runtime |
测试运行时长,通常以秒为单位 | --runtime=60 |
--time_based |
启用按时间运行;达到 runtime 后结束,而不是写满 size 后结束 |
--time_based |
--direct |
是否使用直接 I/O;1 通常用于绕过页缓存,测试更贴近设备 I/O |
--direct=1 |
--ioengine |
I/O 引擎: libaio:Linux异步I/Osync:同步I/Ommap:内存映射I/O psync:POSIX同步I/O io_uring:Linux新的异步I/O接口(需要内核4.18+) Linux 上常用 libaio 或 io_uring; |
--ioengine=libaio |
--group_reporting |
多个 job 时汇总显示结果 | --group_reporting |
--rate |
I/O速率限制 | --rate=100M |
--stonewall |
等待前一个作业完成后再开始 | stonewall |
--rwmixread |
设置混合读写时的读操作比例,取值通常为 0–100;例如设为 70 表示约 70% 读、30% 写。用于 --rw=randrw 或 --rw=rw 等混合模式 |
--rw=randrw --rwmixread=70 |
--rw_sequencer |
控制随机 I/O 序列的生成方式,例如按偏移递增或按随机顺序访问。具体可选值取决于 fio 版本,使用前可查看 fio --cmdhelp=rw_sequencer |
--rw_sequencer=sequential |
--randrepeat |
控制随机 I/O 序列是否可重复:1 表示使用可重复的随机序列,0 表示每次生成不同序列 |
--randrepeat=0 |
--norandommap |
禁用随机偏移映射,允许随机 I/O 重复访问相同区域;启用后可能出现重复访问 | --norandommap |
样例:
fio --name=test \
--filename=/mnt/ssd/fio.test \
--rw=randread \
--bs=4k \
--iodepth=32 \
--numjobs=1 \
--size=4G \
--runtime=60 \
--time_based \
--direct=1 \
--ioengine=libaio \
--group_reporting
fio可以记录实际应用的I/O模式,并在测试中重放:
记录I/O轨迹:
fio --name=trace --filename=/dev/sdb --rw=trace --trace=/tmp/io-trace
bash
重放I/O轨迹:
fio --name=replay --filename=/dev/sdc --rw=replay --replay=/tmp/io-trace
生成图表和报告
虽然fio本身不直接生成图表,但可以将结果输出为JSON格式,然后使用其他工具生成图表:
fio --name=test --rw=randread --bs=4K --size=10G --output-format=json --output=results.json
fio文件
mkdir -p ~/fio-lab
cd ~/fio-lab
nvim learn.fio
# 全局配置:下面的参数会作为默认值应用到后续所有 job。
[global]
# 测试文件路径。当前会在运行 fio 的目录下创建/使用 fio.test。
filename=fio.test
# 测试文件大小。512m 适合快速学习;正式压测建议改大,例如 10g 或更大。
size=512m
# 使用 direct I/O,尽量绕过操作系统页缓存,让结果更接近磁盘本身。
direct=1
# 使用 Linux 异步 I/O 引擎。iodepth > 1 时通常需要异步引擎才能发挥作用。
ioengine=libaio
# 同一个 job 里有多个线程/进程时合并汇总输出;这里保留为通用配置。
group_reporting=1
# 每个 time_based=1 的测试 job 持续运行 20 秒。
runtime=20
# 按时间运行,而不是只完成固定 size 的 I/O 后就退出。
time_based=1
# 预热 3 秒,预热阶段的数据不计入最终统计。
ramp_time=3
# 准备阶段:先顺序写满测试文件,避免后续读测试读到稀疏文件或未初始化区域。
[prepare]
rw=write
bs=1m
iodepth=1
# prepare 只写满 size 指定的 512m 文件,不按 runtime 重复运行。
time_based=0
# 128K 顺序读:观察大块连续读取吞吐能力。
[seq-read-128k]
# 等上一个 job 完成后再运行当前 job,避免不同测试互相干扰。
stonewall
rw=read
bs=128k
iodepth=16
# 4K 随机读:观察小块随机读取 IOPS 和延迟。
[rand-read-4k]
stonewall
rw=randread
bs=4k
iodepth=16
# 4K 随机写:观察小块随机写入 IOPS、带宽和尾延迟。
[rand-write-4k]
stonewall
rw=randwrite
bs=4k
iodepth=16
# 4K 随机混合读写:模拟 70% 读、30% 写的混合负载。
[randrw-70read-30write]
stonewall
rw=randrw
# randrw 模式下读请求占 70%,写请求占 30%。
rwmixread=70
bs=4k
iodepth=16
运行:
fio learn.fio
运行结果分析
运行结果:
prepare: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1
seq-read-128k: (g=1): rw=read, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, (T) 128KiB-128KiB, ioengine=libaio, iodepth=16
rand-read-4k: (g=2): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=16
rand-write-4k: (g=3): rw=randwrite, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=16
randrw-70read-30write: (g=4): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=16
fio-3.36
Starting 5 processes
prepare: Laying out IO file (1 file / 512MiB)
Jobs: 1 (f=1): [_(4),m(1)][100.0%][r=232MiB/s,w=100MiB/s][r=59.5k,w=25.6k IOPS][eta 00m:00s]
prepare: (groupid=0, jobs=1): err= 0: pid=6496: Fri Jun 12 00:19:44 2026
write: IOPS=2370, BW=2370MiB/s (2486MB/s)(512MiB/216msec); 0 zone resets
cpu : usr=13.02%, sys=0.00%, ctx=513, majf=0, minf=10
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,512,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1
seq-read-128k: (groupid=1, jobs=1): err= 0: pid=6497: Fri Jun 12 00:19:44 2026
read: IOPS=44.9k, BW=5615MiB/s (5887MB/s)(110GiB/20001msec)
slat (usec): min=2, max=1078, avg= 4.97, stdev= 6.72
clat (usec): min=107, max=4305, avg=350.89, stdev=67.80
lat (usec): min=147, max=4308, avg=355.85, stdev=67.01
clat percentiles (usec):
| 1.00th=[ 235], 5.00th=[ 281], 10.00th=[ 302], 20.00th=[ 318],
| 30.00th=[ 326], 40.00th=[ 334], 50.00th=[ 343], 60.00th=[ 351],
| 70.00th=[ 359], 80.00th=[ 375], 90.00th=[ 408], 95.00th=[ 457],
| 99.00th=[ 578], 99.50th=[ 619], 99.90th=[ 783], 99.95th=[ 971],
| 99.99th=[ 2540]
bw ( MiB/s): min= 5503, max= 5747, per=100.00%, avg=5619.13, stdev=60.24, samples=37
iops : min=44024, max=45981, avg=44952.54, stdev=481.88, samples=37
lat (usec) : 250=1.85%, 500=95.10%, 750=2.93%, 1000=0.07%
lat (msec) : 2=0.03%, 4=0.01%, 10=0.01%
cpu : usr=3.87%, sys=26.30%, ctx=446742, majf=0, minf=38
IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=100.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=898370,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=16
rand-read-4k: (groupid=2, jobs=1): err= 0: pid=6498: Fri Jun 12 00:19:44 2026
read: IOPS=93.9k, BW=367MiB/s (385MB/s)(7337MiB/20001msec)
slat (nsec): min=772, max=234899, avg=1224.20, stdev=1178.79
clat (usec): min=22, max=5188, avg=168.87, stdev=37.20
lat (usec): min=54, max=5189, avg=170.10, stdev=37.16
clat percentiles (usec):
| 1.00th=[ 114], 5.00th=[ 129], 10.00th=[ 137], 20.00th=[ 145],
| 30.00th=[ 151], 40.00th=[ 157], 50.00th=[ 165], 60.00th=[ 174],
| 70.00th=[ 182], 80.00th=[ 190], 90.00th=[ 206], 95.00th=[ 221],
| 99.00th=[ 265], 99.50th=[ 293], 99.90th=[ 359], 99.95th=[ 396],
| 99.99th=[ 1205]
bw ( KiB/s): min=345338, max=383534, per=100.00%, avg=376024.73, stdev=7578.18, samples=37
iops : min=86334, max=95883, avg=94005.92, stdev=1894.50, samples=37
lat (usec) : 50=0.01%, 100=0.21%, 250=98.23%, 500=1.53%, 750=0.01%
lat (usec) : 1000=0.01%
lat (msec) : 2=0.02%, 4=0.01%, 10=0.01%
cpu : usr=5.22%, sys=16.59%, ctx=571835, majf=0, minf=37
IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=100.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=1878268,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=16
rand-write-4k: (groupid=3, jobs=1): err= 0: pid=6499: Fri Jun 12 00:19:44 2026
write: IOPS=74.7k, BW=292MiB/s (306MB/s)(5836MiB/20001msec); 0 zone resets
slat (nsec): min=841, max=300493, avg=1472.69, stdev=1209.35
clat (usec): min=26, max=31651, avg=212.45, stdev=201.37
lat (usec): min=28, max=31653, avg=213.92, stdev=201.36
clat percentiles (usec):
| 1.00th=[ 137], 5.00th=[ 163], 10.00th=[ 169], 20.00th=[ 180],
| 30.00th=[ 186], 40.00th=[ 192], 50.00th=[ 196], 60.00th=[ 202],
| 70.00th=[ 208], 80.00th=[ 217], 90.00th=[ 229], 95.00th=[ 245],
| 99.00th=[ 791], 99.50th=[ 1188], 99.90th=[ 1991], 99.95th=[ 2212],
| 99.99th=[ 7439]
bw ( KiB/s): min=152798, max=346035, per=100.00%, avg=301335.95, stdev=43057.67, samples=37
iops : min=38199, max=86508, avg=75333.65, stdev=10764.46, samples=37
lat (usec) : 50=0.01%, 100=0.25%, 250=95.59%, 500=2.76%, 750=0.34%
lat (usec) : 1000=0.35%
lat (msec) : 2=0.61%, 4=0.07%, 10=0.02%, 20=0.01%, 50=0.01%
cpu : usr=5.30%, sys=12.93%, ctx=533946, majf=0, minf=42
IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=100.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=0,1494034,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=16
randrw-70read-30write: (groupid=4, jobs=1): err= 0: pid=6500: Fri Jun 12 00:19:44 2026
read: IOPS=44.2k, BW=173MiB/s (181MB/s)(3457MiB/20001msec)
slat (nsec): min=830, max=750803, avg=1695.74, stdev=2624.31
clat (usec): min=17, max=38076, avg=300.03, stdev=395.25
lat (usec): min=67, max=38077, avg=301.73, stdev=395.52
clat percentiles (usec):
| 1.00th=[ 97], 5.00th=[ 121], 10.00th=[ 145], 20.00th=[ 167],
| 30.00th=[ 180], 40.00th=[ 188], 50.00th=[ 196], 60.00th=[ 206],
| 70.00th=[ 219], 80.00th=[ 241], 90.00th=[ 429], 95.00th=[ 1221],
| 99.00th=[ 1909], 99.50th=[ 2057], 99.90th=[ 2409], 99.95th=[ 2704],
| 99.99th=[ 4146]
bw ( KiB/s): min=88342, max=244077, per=100.00%, avg=177531.41, stdev=67074.31, samples=39
iops : min=22085, max=61019, avg=44382.54, stdev=16768.61, samples=39
write: IOPS=18.9k, BW=74.0MiB/s (77.6MB/s)(1481MiB/20001msec); 0 zone resets
slat (nsec): min=923, max=274820, avg=1980.34, stdev=2663.87
clat (usec): min=6, max=4215, avg=136.68, stdev=48.25
lat (usec): min=27, max=4216, avg=138.66, stdev=47.74
clat percentiles (usec):
| 1.00th=[ 49], 5.00th=[ 60], 10.00th=[ 70], 20.00th=[ 93],
| 30.00th=[ 120], 40.00th=[ 137], 50.00th=[ 147], 60.00th=[ 153],
| 70.00th=[ 161], 80.00th=[ 172], 90.00th=[ 182], 95.00th=[ 192],
| 99.00th=[ 237], 99.50th=[ 277], 99.90th=[ 375], 99.95th=[ 433],
| 99.99th=[ 635]
bw ( KiB/s): min=37922, max=104878, per=100.00%, avg=76029.31, stdev=28722.69, samples=39
iops : min= 9480, max=26219, avg=19006.95, stdev=7180.73, samples=39
lat (usec) : 10=0.01%, 20=0.01%, 50=0.38%, 100=7.40%, 250=79.08%
lat (usec) : 500=6.63%, 750=1.17%, 1000=0.99%
lat (msec) : 2=3.90%, 4=0.44%, 10=0.01%, 20=0.01%, 50=0.01%
cpu : usr=5.25%, sys=13.82%, ctx=403341, majf=0, minf=38
IO depths : 1=0.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=100.0%, 32=0.0%, >=64=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=884942,379015,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=16
Run status group 0 (all jobs):
WRITE: bw=2370MiB/s (2486MB/s), 2370MiB/s-2370MiB/s (2486MB/s-2486MB/s), io=512MiB (537MB), run=216-216msec
Run status group 1 (all jobs):
READ: bw=5615MiB/s (5887MB/s), 5615MiB/s-5615MiB/s (5887MB/s-5887MB/s), io=110GiB (118GB), run=20001-20001msec
Run status group 2 (all jobs):
READ: bw=367MiB/s (385MB/s), 367MiB/s-367MiB/s (385MB/s-385MB/s), io=7337MiB (7693MB), run=20001-20001msec
Run status group 3 (all jobs):
WRITE: bw=292MiB/s (306MB/s), 292MiB/s-292MiB/s (306MB/s-306MB/s), io=5836MiB (6120MB), run=20001-20001msec
Run status group 4 (all jobs):
READ: bw=173MiB/s (181MB/s), 173MiB/s-173MiB/s (181MB/s-181MB/s), io=3457MiB (3625MB), run=20001-20001msec
WRITE: bw=74.0MiB/s (77.6MB/s), 74.0MiB/s-74.0MiB/s (77.6MB/s-77.6MB/s), io=1481MiB (1552MB), run=20001-20001msec
Disk stats (read/write):
sdd: ios=4136765/2154907, sectors=288619952/18285136, merge=0/71, ticks=1000530/385245, in_queue=1385779, util=97.89%
片段分析:
seq-read-test: (g=0): rw=read, bs=(R) 256KiB-256KiB, (W) 256KiB-256KiB, (T) 256KiB-256KiB, ioengine=libaio, iodepth=16
fio-3.33
Starting 1 process
Jobs: 1 (f=1): [R(1)][100.0%][r=556MiB/s][r=2281 IOPS][eta 00m:00s]
seq-read-test: (groupid=0, jobs=1): err= 0: pid=12345: Wed Jun 12 10:00:00 2024
read: IOPS=2256, BW=564MiB/s (591MB/s)(33.8GiB/61001msec)
slat (usec): min=4, max=287, avg= 8.23, stdev= 5.12
clat (usec): min=12, max=2403, avg=6982.34, stdev=123.45
lat (usec): min=16, max=2407, avg=6990.57, stdev=123.47
clat percentiles (usec):
| 1.00th=[ 6500], 5.00th=[ 6700], 10.00th=[ 6800], 20.00th=[ 6900],
| 30.00th=[ 6900], 40.00th=[ 7000], 50.00th=[ 7000], 60.00th=[ 7000],
| 70.00th=[ 7100], 80.00th=[ 7100], 90.00th=[ 7200], 95.00th=[ 7300],
| 99.00th=[ 7500], 99.50th=[ 7600], 99.90th=[ 7800], 99.95th=[ 8000],
| 99.99th=[23000]
bw ( KiB/s): min=540000, max=580000, per=100.00%, avg=577892.34, stdev=12345.67, samples=122
iops : min= 2109, max= 2265, avg=2256.23, stdev=48.12, samples=122
lat (usec) : 20=0.01%, 50=0.1%, 100=0.5%, 250=1.0%, 500=2.0%
lat (msec) : 2=5.0%, 4=10.0%, 10=80.0%, 20=1.4%, 50=0.01%
cpu : usr=2.00%, sys=8.00%, ctx=138000, majf=0, minf=123
IO depths : 1=0.1%, 2=0.2%, 4=0.4%, 8=0.8%, 16=98.5%, 32=0.0%
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
issued rwts: total=87808,0,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=16
Run status group 0 (all jobs):
READ: bw=564MiB/s (591MB/s), 564MiB/s-564MiB/s (591MB/s-591MB/s), io=33.8GiB (36.3GB), run=61001-61001msec
- 延迟指标:
- slat: 提交延迟,平均8.23微秒。
- clat: 完成延迟,平均6982.34微秒(约7毫秒)。
- lat: 总延迟,平均6990.57微秒。
- 延迟百分位数: 例如,99.9%的I/O操作延迟小于7800微秒(7.8毫秒)。
- BW: 564MiB/s。这是测试中达到的吞吐量。
- IOPS: 2256次/秒。这表示存储系统每秒可以处理约2256次I/O操作。
- CPU使用率: 用户空间2%,系统空间8%,总CPU使用率约10%。
- I/O队列深度: 主要集中在16,与测试中设置的iodepth=16相符。
常见性能问题判断
高CPU使用率+低IOPS: 可能是CPU成为瓶颈,或I/O模式效率低下。
低CPU使用率+低IOPS: 可能是存储设备本身性能不足。
高延迟+低IOPS: 可能是存储系统存在内部瓶颈,如缓存命中率低或RAID控制器性能不足。
波动大的性能指标: 可能是系统配置不当或存在资源竞争。

浙公网安备 33010602011771号