赞
踩
fio 是一个开源压力测试工具,主要用来测试硬盘 io 性能。这个工具的可定制性非常强,可以根据测试者的想法进行各种混合 io 测试,它支持 13 种不同类型 io 引擎(libaio、sync、mmap、posixaio、network 等等)。它可以测试块设备或文件,可以通过多线程或进程模拟各种 io 操作,可以测试统计 iops、带宽和时延等性能。我们主要使用 fio 工具进行存储性能测试。
虽然现在大范围的使用 SSD,但是在一些存储系统中 HDD 还是大批量被使用,毕竟便宜;SSD 固态硬盘通过电存储,HDD 机械硬盘通过机械运动存储,很明显 SSD 在存储性能上有先天优势;影响 SSD 性能的主要因素有存储主控芯片的设计、闪存单元设计,当然还有工艺水平。目前 SSD 主控芯片设计最前沿的技术在 Intel 手上,Intel SSD 性能是目前最好的。
针对 HDD 机械硬盘的性能,数据的读写有 95% 的耗时是消耗在机械运动上,一次数据操作为一次 IO 服务,它包括以下几个环节:
对于一个确定的磁盘,旋转延迟保持不变,而内部接口传送时间和外部接口传输时间微秒级别,基本可以忽略不计,那么影响 HDD 性能核心就是寻道时间;
对磁盘性能的度量有两个指标:IOPS,IO 吞吐/带宽;
IOPS 指的是每秒可以完成的 IO 服务的次数,一次 IO 服务主要的耗时是寻道时间上,如果是大量的随机 IO,那么每次寻道时间都处于上限值,IOPS 下降;
IO 吞吐表示在指定时间内,完成的 IO 读写数据字节数,它的值和每次 IO 读写的数据大小有密切关系,也从而会影响 IOPS,比如每次读写数据块较大,从而最大化的降低了寻道带来的开销,从而提高吞吐,但是此时 IOPS 就会减小;
实例说明:写入 10000 个大小为 1kb 的文件到硬盘上,耗费的时间要比写入 10 个 1mb 大小的文件多得多。
对磁盘性能 IOPS 和 IO 吞吐的追求与业务有密切关系,比如大文件存储希望吞吐可以做到最高,而小文件或随机读写的业务更加追求 IOPS;如果业务对吞吐和 IOPS 都有一定依赖,那么就需要找到一个合适的 blocksize 来设置每次 IO 大小,从而在 IOPS 和吞吐之间均衡,即(IOPS × block_size = IO 吞吐);
提供 IO 性能的方法:
NOTE:在计算机程序设计当中,往往从时间局部性、空间局部性及缓存命中率几个角度对程序进行性能优化,而且在计算机领域,这种局部性原理都是通用的。
Ubuntu 环境
sudo apt-get install fio gunplot
嵌入式平台或源码编译
fio 源码下载地址:https://github.com/axboe/fio/tags
git clone https://github.com/axboe/fio.git
./configure --cc=arm-linux-gnueabihf-gcc --prefix=./build
make
make install
确认系统安装是否有 libaio 和 libaio-devel,如果没有安装这两个包,fio 工具不能使用异步的 libaio 引擎。如果在安装 fio 前未安装 libaio 和 libaio-devel,那么安装了 libaio 和 libaio-devel 后需要重新编译安装 fio,不然也无法使用异步引擎 libaio。
fio 是可以根据测试要求配置不同参数进行 io 性能测试的工具,可配置的参数有上百个,几乎可以覆盖所有的 io 模型,详细参数可查询 fio 的官网获取:https://fio.readthedocs.io/en/latest/fio_doc.html。
存储性能测试中常用的参数如下所示:
参数 | 参数值 | 解释 |
---|---|---|
filename | 设备名或文件名如:裸设备 /dev/sdb,文件 /home/test.img | 定义测试对象,一般是设备或者文件。如果想测试裸盘设备 /dev/sdb,可以设置 filename=/dev/sdb;如果想要测试文件系统性能,则可以设置 filename=/home/test.img |
name | 测试名称 | 定义测试名称。必填项,本地 fio 测试的名称,对性能没有影响。 |
rw | 测试类型,可选值有:read,write,rw,randread,randwrite,randrw | 定义测试的读写类型:read-顺序读,write-顺序写,rw(readwrite)- 混合顺序读写,randread- 随机读,randwrite-随机写,randrw-混合随机读写。对于读写混合类型来说,默认读写比例为 1:1 |
rwmixwrite rwmixread | 混合读写中读写占用比例,可选值为[0,100] | 定义在混合读写模式中,写或读所占的比例。举例来说,rwmixread 值为 10,则表示读写比为 10:90。 |
ioengine | io 引擎选择,可选值有 sync、libaio、psync、vsync、mmap 等等 | 定义 fio 如何下发 io 请求。sync:基本的 read、write io。 libaio:linux 原生的异步 io,linux 只支持 non-buffer 情况下的排队操作。默认值为 psync,该模式为同步 io 模型,异步 io 模型 libaio,一般结合 direct=1 使用。 |
direct | 0 或 1 | 定义是否使用 direct io:值为 0,表示使用 buffered io;值为 1,表示使用 direct io。 |
bs | 带单位数字 | 定义 io 的块大小,单位是 k,K,m,M。默认值为 4k。 |
numjobs | 正整数 | 定义测试的进程/线程数,默认值为 1,如果指定了 thread 参数,则使用单进程多线程模式,否则使用多进程模式。 |
iodepth | 正整数 | 定义每个进程/线程可以同时下发的 io 任务数,默认为 1,适用于异步 io 场景,同步 io 场景要等前一个 io 任务完成才能下发下一个任务,所以 iodepth 并不起作用。 |
其他相关参数:
参数 | 参数值 | 解释 |
---|---|---|
size | 整数 定义 io 操作的数据量,除非指定了 runtime 这个参数,fio 会将指定大小的数据量全部读写完成,才会停止测试。 | 该参数的值,可以是带单位的数字,比如 size=2G,表示读/写的数据量为 2G;也可以是百分比,比如 size=20%,表示读写的数据量占该设备/文件的 20% 的空间;除了表示 io 操作数据量,size 还表示 io 操作的范围,与 offset 配合一起读写[offset,offset+size]范围内的数据 |
runtime | 正整数 | 定义测试时间,以秒为单位。对于指定的文件设备,如果在测试时间内完成了读写操作,则会保持相同的负载循环进行读写。 |
ramp_time | 正整数 | 定义测试的热身时间,以秒为单位。热身时间不计入测试统计。 |
thinktime | 正整数 | 设置当一个 io 完成后,等待多少时间再下发另一个 io,单位为微妙。一般用于模拟应用等待、处理事务等。 |
time_based | NA | 默认值为 0,如果设置了该参数,则本次测试会一直运行到 runtime 结束为止。 |
offset | 定义测试起始位置 | fio 在硬盘测试的起始位置,配合顺序读写使用。不同的 offset 对 HDD 的性能影响大,对 SSD 理论无影响。 |
group_reporting | NA | 对于测试并发数大于 1 的情况,如果想要将所有进程/线程的测试结果进行统计,输出总的测试结果,而不是各自单独输出,可以使用此参数。 |
cpus_allowed | 指定 cpu 的 core | fio 测试的所有进程/线程可以选择的 cpu 核,可以是单独一个或者多个核,也可以是一个范围。 |
output | 结果输出路径 | 结果输出文件,默认直接把结果打印到命令行,设置后会把结果写进目标文件,命令行则不显示结果。 |
# fio -name=mytest \
-filename=/dev/sdb \
-direct=1 \
-iodepth=20 \
-thread \
-rw=randread \
-ioengine=libaio \
-bs=16k \
-size=5G \
-numjobs=2 \
-runtime=300 \
-group_reporting \
顺序读:
fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=read -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting
顺序写:
fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=write -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting
随机写:
fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=randwrite -ioengine=libaio -bs=1k -size=5G -numjobs=2 -runtime=300 -group_reporting
混合顺序读写:
fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=rw -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting
混合随机读写:
fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=randrw -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting
测试文件系统
fio -name=mytest -filename=/test/test.img -direct=1 -iodepth=20 -thread -rw=randread -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting
fio 也可以通过执行 job 方式,同时执行多个不同的文件,使用很少的命令执行测试。测试命令:fio job。
job 文件的编写参考:
[global]
ioengine=libaio
direct=1
size=8g
filesize=500g
time_based
rw=randrw
rwmixread=70
bs=4k
runtime=120
[job1]
filename=/dev/sdb
numjobs=16
iodepth=1
[job2]
filename=/dev/sdc
numjobs=16
iodepth=1
# fio -name=hdd7k -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=rw -ioengine=libaio -bs=4k -size=10G -numjobs=10 -runtime=300 -group_reporting
hdd7k: (g=0): rw=rw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=20
...
fio-3.29-7-g01686
Starting 10 threads
Jobs: 10 (f=9): [f(10)][100.0%][r=10.9MiB/s,w=10.7MiB/s][r=2785,w=2742 IOPS][eta 00m:00s]
hdd7k: (groupid=0, jobs=10): err= 0: pid=73595: Thu Jan 13 11:19:06 2022
read: IOPS=3258, BW=12.7MiB/s (13.3MB/s)(3820MiB/300065msec)
slat (usec): min=2, max=547, avg= 6.47, stdev= 4.04
clat (usec): min=27, max=4722.3k, avg=33300.91, stdev=77842.79
lat (usec): min=47, max=4722.3k, avg=33307.62, stdev=77842.14
clat percentiles (usec):
| 1.00th=[ 725], 5.00th=[ 1745], 10.00th=[ 2737],
| 20.00th=[ 3228], 30.00th=[ 3687], 40.00th=[ 4621],
| 50.00th=[ 5866], 60.00th=[ 7832], 70.00th=[ 14615],
| 80.00th=[ 39060], 90.00th=[ 116917], 95.00th=[ 135267],
| 99.00th=[ 333448], 99.50th=[ 463471], 99.90th=[ 843056],
| 99.95th=[1052771], 99.99th=[1686111]
bw ( KiB/s): min= 200, max=91945, per=100.00%, avg=13408.99, stdev=1441.98, samples=5840
iops : min= 50, max=22985, avg=3351.87, stdev=360.48, samples=5840
write: IOPS=3261, BW=12.7MiB/s (13.4MB/s)(3823MiB/300065msec); 0 zone resets
slat (usec): min=3, max=532, avg= 6.68, stdev= 4.17
clat (usec): min=38, max=4725.1k, avg=28027.57, stdev=69203.56
lat (usec): min=50, max=4725.2k, avg=28034.48, stdev=69202.94
clat percentiles (usec):
| 1.00th=[ 848], 5.00th=[ 1860], 10.00th=[ 2802],
| 20.00th=[ 3294], 30.00th=[ 3752], 40.00th=[ 4621],
| 50.00th=[ 5800], 60.00th=[ 7439], 70.00th=[ 11994],
| 80.00th=[ 27919], 90.00th=[ 111674], 95.00th=[ 122160],
| 99.00th=[ 265290], 99.50th=[ 392168], 99.90th=[ 784335],
| 99.95th=[ 977273], 99.99th=[1904215]
bw ( KiB/s): min= 208, max=91939, per=100.00%, avg=13475.63, stdev=1444.81, samples=5818
iops : min= 52, max=22984, avg=3368.52, stdev=361.18, samples=5818
lat (usec) : 50=0.01%, 100=0.01%, 250=0.06%, 500=0.32%, 750=0.53%
lat (usec) : 1000=0.74%
lat (msec) : 2=4.20%, 4=28.23%, 10=32.04%, 20=8.83%, 50=8.44%
lat (msec) : 100=4.75%, 250=10.40%, 500=1.10%, 750=0.23%, 1000=0.07%
lat (msec) : 2000=0.05%, >=2000=0.01%
cpu : usr=0.18%, sys=0.45%, ctx=964464, majf=0, minf=210
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 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.0%, 32=0.1%, 64=0.0%, >=64=0.0%
issued rwts: total=977794,978699,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=20
Run status group 0 (all jobs):
READ: bw=12.7MiB/s (13.3MB/s), 12.7MiB/s-12.7MiB/s (13.3MB/s-13.3MB/s), io=3820MiB (4005MB), run=300065-300065msec
WRITE: bw=12.7MiB/s (13.4MB/s), 12.7MiB/s-12.7MiB/s (13.4MB/s-13.4MB/s), io=3823MiB (4009MB), run=300065-300065msec
Disk stats (read/write):
sdb: ios=480787/482996, merge=492621/492299, ticks=6820973/5217066, in_queue=7483700, util=100.00%
Fio 正在运行时,会显示任务的状态信息,如下:
# 运行中
Jobs: 10 (f=10): [M(10)][2.7%][r=2508KiB/s,w=2856KiB/s][r=627,w=714 IOPS][eta 04m:52s]
# 已完成
Jobs: 10 (f=9): [f(10)][100.0%][r=10.9MiB/s,w=10.7MiB/s][r=2785,w=2742 IOPS][eta 00m:00s]
P Thread setup, but not started.
C Thread created.
I Thread initialized, waiting or generating necessary data.
p Thread running pre-reading file(s).
/ Thread is in ramp period.
R Running, doing sequential reads.
r Running, doing random reads.
W Running, doing sequential writes.
w Running, doing random writes.
M Running, doing mixed sequential reads/writes.
m Running, doing mixed random reads/writes.
D Running, doing sequential trims.
d Running, doing random trims.
F Running, currently waiting for fsync(2).
V Running, doing verification of written data.
f Thread finishing.
E Thread exited, not reaped by main thread yet.
- Thread reaped.
X Thread reaped, exited with an error.
K Thread reaped, exited due to signal.
hdd7k: (groupid=0, jobs=10): err= 0: pid=73595: Thu Jan 13 11:19:06 2022
定义的任务名称,groupid,聚合的任务数,最后一个错误 id(0 表示没有错误)和 pid,完成时间。
read/write/trim:IOPS 表示每秒平均 IOPS;BW 表示平均带宽,BW 后面的数据是 2 进制带宽,括号中是 10 进制带宽;最后两个值表示(二进制总 IO 大小/总运行时间)
slat:提交任务延迟(submission latency),也就是异步 IO 指令生成消耗的时间(min 最小时间,max 最大时间,avg 平均时间,stdev 标准差)。对于同步 I/O,这一行不会显示,因为 slat 实际上是完成延迟(completion latency)。这个值的单位可以是纳秒、微秒或毫秒,由 fio 自动选择合适的单位。在 --minimal 模式下,延迟总是以微秒计算。
clat:完成延迟(completion latency),这表示从提交 IO 指令到内核再到 IO 指令全部运行所需要的时间,不包括提交任务延迟(submission latency);同步 IO 情况下 clat 通常等于(或非常接近)0,因为从提交到完成仅仅表示 CPU 时间(IO 指令已经生成了)。
lat:总延迟(total latency),这表示从 fio 创建 IO 单元到 IO 指令全部运行所花费的时间。
clat percentiles:完成延迟百分比分布,注意,从源码来看,它不是 slat + clat;它有自己的结构体描述。理解思路参考下面的 lat(nsec/usec/msec)。
bw:基于样本的带宽统计数据。最好是在同一磁盘同一组线程中取样,才具有实际意义,因为磁盘竞争访问才符合实际情况。
iops:基于样本的 IOPS 统计数据。同 bw。
lat(nsec/usec/msec):IO 完成延迟(I/O completion latencies)的分布情况,这表示从 IO 离开 fio 到它完成的时间。与上面单独的 read/write/trim 部分不同,这里和其余部分中的数据适用于报告组的所有 IO。50=0.01% 意味着在 50us 以下完成了 0.01% 的 IO,100=0.01% 意味着 0.01% 的 IO 需要 50 到 99us 才能完成。依次类推。
cpu:CPU 使用率。 用户和系统时间,以及该线程所经历的上下文切换次数、系统和用户时间的使用情况,最后是主要和次要页面错误的数量。 CPU 利用率数字是报告组中任务的平均值,而上下文和故障计数器是求和的。
IO depths:任务生命周期内 IO 深度的分布。 这些数字被分成 2 的幂,每个条目涵盖了从该值到低于下一个条目的深度,例如,16= 涵盖了从 16 到 31 的深度。请注意,深度分布条目所覆盖的范围可能与等效的 submit/complete 分布条目所覆盖的范围不同。
IO submit:在一个提交调用中提交了多少个 IO 块。 每个条目表示这个数量,直到前一个条目。例如,4=100% 意味着我们每次提交调用提交的 IO 在 1 到 4 之间。 请注意,提交分布条目所覆盖的范围可以不同于等效深度分布条目所覆盖的范围。
IO complete:同理 submit,表示完成的 IO 块。
IO issued rwt:发布的 read/write/trim 请求数量以及缺少和丢掉的数量。
IO latency:这些值用于 latency_target 和相关选项。 当使用这些选项时,该片段描述满足指定时延目标所需的 IO 深度。
以上是报告中的详细参数说明,下面对总结报告数据说明一下:
Run status group 0 (all jobs):
READ: bw=12.7MiB/s (13.3MB/s), 12.7MiB/s-12.7MiB/s (13.3MB/s-13.3MB/s), io=3820MiB (4005MB), run=300065-300065msec
WRITE: bw=12.7MiB/s (13.4MB/s), 12.7MiB/s-12.7MiB/s (13.4MB/s-13.4MB/s), io=3823MiB (4009MB), run=300065-300065msec
Disk stats (read/write):
sdb: ios=480787/482996, merge=492621/492299, ticks=6820973/5217066, in_queue=7483700, util=100.00%
参考:
FIO 磁盘性能测试
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。