fio
文章转自:
https://static.mtyun.com/library/fio-introduction
fio是Jens Axboe大神编写的性能测试工具,可以用来测试CPU、网络以及存储等多种设备的性能数据,目前开源在github上(https://github.com/axboe/fio)。 fio的来由是axboe大神在日常性能测试工作中,发现很多重复、繁琐的工作,于是程序员的“惰性”大发,开发了fio这一测试工具——也可称之为框架,因为你可以编写自己的插件。
详细见:https://linux.die.net/man/1/fio
源码地址:https://github.com/axboe/fio/releases
fio安装
apt update apt-get install libaio-dev -y wget https://git.kernel.dk/cgit/fio/snapshot/fio-3.27.tar.gz tar -zxvf fio-3.27.tar.gz cd fio-3.27 ./configure make && mak install
fio架构简介
fio主要分为job管理、ioengine和数据收集等模块,其中ioengine是fio的核心。 fio ioengine采用类似于vfs的架构,即定义一组公共的操作方法(ioengineops),具体的ioengine可以通过实现ioengineops等指定接口,插入到fio框架中,并得以成功执行。 这一框架可通过下图得以简示:
下面以fio 2.1.1版本和以下配置文件(example.conf)为例,来看fio的主要流程:
[global] group_reporting=1 iodepth=4 bsrange=4k-16k ioengine=libaio nrfiles=20 size=250G filesize=1m-256m numjobs=32 runtime=1000 create_serialize=1 [random-read] name=random-read rw=randread directory=/tmp/fiotest
基于这一配置文件,fio example.conf
的大执行流程为:
- 解析配置文件。
- 准备测试文件,创建nrfiles个文件,命名为“$jobname.$[1~numjobs].$[1~nrfiles]”,总共会创建nrfiles*numjobs个文件。如果create_serialize=0,文件的创建会在各个进程中进行,于是文件创建动作相互交织,文件的分布更为随机。
- 创建numjobs个进程或者线程(thread=0/1参数可控),执行fio任务。
- 在每个fio任务中,tdioinit()会钓鱼ioengine的init()函数,进行ioengine的初始化,随后执行其他一些初始化工作,比如设置计时器等。
- 每个fio任务中,代码执行到do_io(),开始执行真正的IO工作。
部分参数
filename=/dev/vdb 支持文件系统或者裸设备,-filename=/dev/sdb或-filename=/data/fiotest direct=1 测试过程绕过机器自带的buffer,使测试结果更真实 rw=randwread 测试随机读的I/O rw=randwrite 测试随机写的I/O rw=randrw 测试随机混合写和读的I/O rw=read 测试顺序读的I/O rw=write 测试顺序写的I/O rw=rw 测试顺序混合写和读的I/O bs=4k 单次io的块文件大小为4k bsrange=512-2048 同上,提定数据块的大小范围 size=5g 本次的测试文件大小为5g,以每次4k的io进行测试 numjobs=30 本次的测试线程为30 runtime=1000 测试时间为1000秒,如果不写则一直将5g文件分4k每次写完为止 ioengine=psync io引擎使用pync方式,如果要使用libaio引擎,需要yum install libaio-devel包 rwmixwrite=30 在混合读写的模式下,写占30% group_reporting 关于显示结果的,汇总每个进程的信息 此外 lockmem=1g 只使用1g内存进行测试 zero_buffers 用0初始化系统buffer nrfiles=8 每个进程生成文件的数量
fio输出解析
以loop块设备的fio输出结果为例,图示各个部分的含义:
结果参数
io=执行了多少M的IO bw=平均IO带宽 iops=IOPS runt=线程运行时间 slat=提交延迟 clat=完成延迟 lat=响应时间 bw=带宽 cpu=利用率 IO depths=io队列 IO submit=单个IO提交要提交的IO数 IO complete=Like the above submit number, but for completions instead. IO issued=The number of read/write requests issued, and how many of them were short. IO latencies=IO完延迟的分布 io=总共执行了多少size的IO aggrb=group总带宽 minb=最小.平均带宽. maxb=最大平均带宽. mint=group中线程的最短运行时间. maxt=group中线程的最长运行时间. ios=所有group总共执行的IO数. merge=总共发生的IO合并数. ticks=Number of ticks we kept the disk busy. io\_queue=花费在队列上的总共时间. util=磁盘利用率
fio如何测量IOPS
使用fio测量存储设备或系统的IOPS值,使fio的常用使用场景之一。 那么fio使如何测量IOPS的?
在job开始之前的initiou()中,fio初始化三个队列,分别是iourequeues、ioufreelist和iouall。前者大小被初始化为iodepth最近的2^x,后二者大小被初始化为iodepth。 iourequeues实际上被实现为环,初始状态为head=tail=0。 ioufreelist和iouall都是FIFO,初始状态为nr=0;然后在initiou()的后面部分,fio通过iouqpush()往二者中放入相同的iodepth个新建的iou,flags均为IOUFFREE,并且会调用ioengine的iouinit()。 并且对于ioufreelist,在iou完成等情况发生时,fio通过putiou()回收iou,可被用来重复使用。
在doio时,会实现getiou,先检查如果iourequeues非空,则从其中取;如果ioufreelist非空,则从其中取。当然开始时一定是从后者成功取出iou。 tdioqueue(td, iou),调用ioengine的排队函数,以libaio ioengine为例,libaio的排队函数会将iou放入事先初始化的iocbs[]和ious[]数组,数组的大小也是iodepth。 当然如果libaio的队列已满,libaio的排队函数返回FIOQBUSY;如果iou操作是sync类型或trim,则返回FIOQCOMPLETED;否则排入队列并返回FIOQQUEUED。 实际上IOPS的计数是由td->ts.totaliou变量表示的。在返回FIOQCOMPLETED、FIOQQUEUED时,totaliou++。这就是fio的IOPS是如何被记录的。
个人使用
主要测试IOPS、吞吐、时延三个数据,Q表示IO深度,T表示线程数
IOPS 性能 > 随机读 4K Q128T4 100G > fio -direct=1 -iodepth=128 -thread -rw=randread -ioengine=libaio -bs=4k -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 随机写 4K Q128T4 100G > fio -direct=1 -iodepth=128 -thread -rw=randwrite -ioengine=libaio -bs=4k -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 吞吐性能 > 顺序读 1M Q32T4 100G > fio -direct=1 -iodepth=32 -thread -rw=read -ioengine=libaio -bs=1m -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 顺序写 1M Q32T4 100G > fio -direct=1 -iodepth=32 -thread -rw=write -ioengine=libaio -bs=1m -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 时延性能 > 随机读 4K Q1T1 100G > fio -direct=1 -iodepth=1 -thread -rw=randread -ioengine=libaio -bs=4k -size=100G -numjobs=1 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 随机写 4K Q1T1 100G > fio -direct=1 -iodepth=1 -thread -rw=randwrite -ioengine=libaio -bs=4k -size=100G -numjobs=1 -runtime=1000 -group_reporting -directory=/home/smb -name=Test 混合读写性能 > R2:W8 256K Q32T4 100G > fio -direct=1 -iodepth=32 -thread -rw=rw -ioengine=libaio -rwmixread=20 -bs=256k -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test R8:W2 256K Q32T4 100G > fio -direct=1 -iodepth=32 -thread -rw=rw -ioengine=libaio -rwmixread=80 -bs=256k -size=100G -numjobs=4 -runtime=1000 -group_reporting -directory=/home/smb -name=Test
注意事项
1、同时在测试其他的设备裸设备时,一定要提前确定好此裸设备上是否有重要的数据,一定要重要的设备提前做好备份。fio会直接覆盖该磁盘空间中文件,请避开直接使用系统所在设备
2、建议使用顺序I/O和较大的blocksize 来测试设备的通吐量和延迟
3、建议使用随机I/O和较小的blocksize来测试设备的IOPS和延迟
4、在配置numjobs和iodepth 测试底层存储性能时,建议要深入了解应用到底采用的是同步的io还是异步的io(是多进程并发i/o请求还是一次提交一批的i/o的请求)