1. 什么是 fio ?
fio(Flexible I/O Tester)是一个用于测试和评估 I/O 负载和性能的工具,具有高度的可配置性和灵活性。它可以模拟不同类型的负载,包括随机和顺序读写,随机和顺序混合读写,随机和顺序的深度嵌套目录结构等,同时可以产生各种不同的 I/O 负载模式,例如随机性、对齐和分配大小、顺序性等特征,以便更好地模拟实际应用程序中的 I/O 负载。fio 在评估和比较存储系统方面非常有用,例如硬盘驱动器、固态硬盘、闪存存储器、网络存储器等。 - ChatGPT
本文主要介绍在 Linux 上使用 fio 测试磁盘/文件系统的性能,fio 的其他功能本文不关注。
对磁盘和文件系统的测试可以认为是一回事,因为文件系统挂载后在使用方面与本地磁盘没有什么区别,所以本文不会仔细区分这两个场景。
本文所使用的是系统为 CentOS 8.4.2105,其他发行版与本文可能会有出入,本文不做解释,可自行查看文档 fio - Flexible I/O tester。
2. 安装 fio
安装 fio 有几种常用方法,可以直接通过包管理工具(如 yum、dnf 等)安装,或者是手动编译安装,还有就是官方有提供编译好的 fio 二进制文件下载。
大部分场景中,我们使用包管理器安装就可以了。不过要知道,通常包管理器中的版本是要落后于官方最新版本的,例如我这里:
1 | cat /etc/redhat-release |
可以看到我这里的系统为 CentOS 8.4.2105,yum 中提供的 fio 版本为 3.19。而截止本文撰写时间,官方最新版是 3.35。
可以通过 fio 的开源仓库 查到,fio 3.19 的发布时间是 2020/05/13,fio 3.35 的发布时间是 2023/05/24,yum 中的版本足足落后了 3 年多。
所以这小节几种 fio 安装方法都介绍,读者根据自己的使用场景选择就好,即简单使用可以直接包管理器安装,追求新版本特性则可以手动编译安装或者下载官方提供的 fio 二进制文件。
2.1. 包管理器安装
包管理器安装 fio 非常简单,以 CentOS 中的 yum 为例:
1 | yum install fio |
2.2. 手动编译安装
2.2.1. 下载 fio 源码
fio 的源码仓库有好几个:
- https://git.kernel.dk/cgit/fio/
- https://git.kernel.org/pub/scm/linux/kernel/git/axboe/fio.git
- https://github.com/axboe/fio.git
也可以从 https://brick.kernel.dk/snaps/ 中下载指定版本的 fio 的 tar 包,解压出源码。
这里以从 GitHub 仓库获取为例:
1 | git clone https://github.com/axboe/fio.git |
然后我们进入源码目录:
1 | cd fio/ |
2.2.2. 配置 fio
1 | ./configure |
-
这里其实是有一些选项可以配置的,不过一般默认情况已经能够满足绝大部分场景,这里就不解释了,就列个清单看看:
1 | --prefix= Use this directory as installation prefix |
2.2.3. 编译 fio
依次执行:
1 | make |
1 | make install |
注意 make install
可能需要 root 权限。
然后就编译安装完成了。
2.3. 下载二进制文件
官方提供了各种系统的 fio 二进制文件下载,这里也是有最新版的。我这里直接把官方链接贴过来了,自行下载就好。
唯一要注意的是记得把下载好的 fio 二进制文件移动到 $PATH
里,不然用起来太麻烦。
Debian:
Starting with Debian “Squeeze”, fio packages are part of the official Debian repository. https://packages.debian.org/search?keywords=fio .
Ubuntu:
Starting with Ubuntu 10.04 LTS (aka “Lucid Lynx”), fio packages are part of the Ubuntu “universe” repository. https://packages.ubuntu.com/search?keywords=fio .
Red Hat, Fedora, CentOS & Co:
Starting with Fedora 9/Extra Packages for Enterprise Linux 4, fio packages are part of the Fedora/EPEL repositories. https://packages.fedoraproject.org/pkgs/fio/ .
Mandriva:
Mandriva has integrated fio into their package repository, so installing on that distro should be as easy as typing urpmi fio
.
Arch Linux:
An Arch Linux package is provided under the Community sub-repository: https://www.archlinux.org/packages/?sort=&q=fio
Solaris:
Packages for Solaris are available from OpenCSW. Install their pkgutil tool (http://www.opencsw.org/get-it/pkgutil/) and then install fio via pkgutil -i fio
.
Windows:
Beginning with fio 3.31 Windows installers are available on GitHub at https://github.com/axboe/fio/releases. Rebecca Cran [rebecca@bsdio.com](mailto:rebecca@bsdio.com) has fio packages for Windows at https://bsdio.com/fio/ . The latest builds for Windows can also be grabbed from https://ci.appveyor.com/project/axboe/fio by clicking the latest x86 or x64 build and then selecting the Artifacts tab.
BSDs:
Packages for BSDs may be available from their binary package repositories. Look for a package “fio” using their binary package managers.
2.4. 验证
不管你使用的是哪种安装方法,安装之后的效果应该是一样的。
我们可以使用 fio -v
查看已安装 fio 的版本,正确输出版本信息就说明安装成功了。
1 | fio -v |
3. fio 的使用
fio 的使用非常简单,命令行用法如下:
1 | fio [options] [jobfile] ... |
这里面主要有两个需要关注的点:
options
:命令行选项,配置 fio 运行时的各种参数。jobfile
:作业文件,可以有多个。其值可以为-
,则表示从标准输入读取。
作业文件 jobfile
中描述了 fio 要做什么。所以我们使用 fio 进行 I/O 测试前,必须先写至少一个作业文件。
下面分别来说,fio 进行 I/O 测试的方法,以及如何编写作业文件。
注意本文介只介绍比较常用的部分,更多内容可以看 fio 官方文档。
3.1. 几种 fio 的使用方法
fio 有三种常用的使用方法。
一种是仅使用命令行设定参数和选项,即:
1
fio [options]
一种是仅使用作业文件:
1
fio [jobfile]
也可以混合使用,就如一开始说的那样:
1
fio [options] [jobfile] ...
注意混合使用的时候,命令行的参数优先级大于作业文件中设定的,即如果命令行和作业文件中设定了相同的参数,则会使用命令行中指定的值。
3.2. 从一个例子开始
下面两个例子,使用 fio 测试文件系统顺序写大文件性能,两个例子完全等价。
3.2.1. 仅使用命令行
1 | fio --name=seqwrite --ioengine=sync --rw=write --bs=1M --size=10G --numjobs=1 --time_based --runtime=120s --eta=never --filename=/data/testfile.dat |
3.2.2. 仅使用作业文件
先编写一个 fio 作业文件,我这里名为 seqwrite.fio
:
1 | [global] |
然后在命令行使用作业文件进行测试:
1 | fio seqwrite.fio |
3.2.3. 上述例子的理解
上面 3.2.1 和 3.2.2 的代码配置了一个顺序写的测试负载,写入一个名为 testfile.dat
的文件中,使用 1MB 的块大小,写入 10GB 的数据,持续运行 120 秒。
这里不解释这些参数的含义,不过我们可以知道几件事:
命令行的参数与作业文件中的属性可以一一对应。例如命令行参数项
--ioengine
对应作业文件中的ifengine
;命令行中的测试名字--name
对应作业文件中的中括号[]
里的部分,如这里命令行参数--name=seqwrite
对应作业文件中的[seqwrite]
(注意[global]
是特殊的,指定的作业文件内所有测试的通用参数,如果下面某个作业的参数与[global]
中指定的冲突,则以作业内配置的为准)。作业文件内可以有多组测试,例如我们可以把
seqwrite.fio
修改为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21[global]
ioengine=sync
direct=1
time_based
eta=never
[seqwrite]
filename=/data/testfile.dat
rw=write
bs=1M
size=10G
numjobs=1
runtime=120s
[randwrite]
filename=/data/randtestfile.dat
rw=randwrite
bs=1M
size=10G
numjobs=1
runtime=120s这样就在作业文件内增加了一组随机写测试。
filename
属性指定的文件(目录),应当在我们要测试的磁盘/文件系统内,测试的才是指定存储系统的性能。
还有一些用法这里没有介绍,因为我认为不具备什么价值(很少使用),简单列出一下:
- 仅命令行也可以进行多组测试,但不建议使用。因为这样使用会导致命令语句非常长,参数非常多,而且很容易写错,缺点很多。
- fio 同时指定多个作业文件,每个作业文件用
-f
参数指定,例如fio -f jobfile1.fio -f jobfile2.fio
,但也不常使用。主要原因一方面是复杂度增加,一方面是多个作业文件是并发执行的,可能会出现资源竞争、测试数据互相干扰等问题。 - 这里没有给出混合使用命令行参数和作业文件的例子,其使用场景在下面的建议中说明。
3.3. 几种 fio 使用方法的选取建议
通过前面的介绍,我这里总结了几点 fio 使用方法的选取建议:
- 简单的、临时的测试,可以采取仅命令行的操作,方便快捷。
- 当需要进行复杂的测试或者需要定期运行一组测试时,使用作业文件更加适合。
- 当我们使用作业文件时,临时想修改某个属性(参数)又不想修改作业文件时,可以临时在命令行上添加此参数以覆盖作业文件内的。
不过要注意,有一些参数只能在命令行中使用,例如 --minimal
参数会最简化 fio 的输出,即便我们使用作业文件,此参数也只能体现到命令行中,例如:
1 | fio --minimal myjobfile.job |
4. fio 测试存储系统的相关属性(参数)
fio 的可选参数相当多,这里仅列出与测试存储系统性能相关的,更多选项与用法见 fio 官方文档。
下面给出 fio 测试存储系统性能的必要参数与可选值(注意这里给出的都是作业文件中的名称,在命令行中通常加前缀 --
):
ioengine
=str:定义作业如何向文件发出 I/O。测试存储系统有以下几个常用取值:sync
:同步 I/O,每次操作都会等待 IO 完成后再进行下一次操作。适用于简单的 IO 测试,但是性能较差。psync
:同步 I/O,与 sync 类似,但是预提交缓存模式。该模式在 IO 操作时会先将数据存放在内存中的缓存区中,然后定期(或者每次 I/O 操作后)将缓存区的内容提交到磁盘中,以此提高磁盘 I/O 的性能。pvsync
:同步 I/O,类似 psync,但能够更好地利用预提交缓存机制,通常会有更好的性能表现。适用于需要测试高并发 I/O 性能的场景,如云计算、虚拟化等环境。io_uring
:使用 io_uring 实现异步 I/O,相比 libaio 和 posixaio(这里均未列出),具有更高的性能和更少的上下文切换。mmap
:同步 I/O,内存映射模式,将数据文件映射到内存中,以便快速访问文件内容。适用于对顺序读写的测试,可以实现很高的 IOPS。
direct
=bool:布尔值(默认为 false)。如果为 true,则使用直接 I/O,反之使用缓冲 I/O。readwrite
=str,rw
=str:指定 I/O 模式。常用取值有以下几个:read
:顺序读。write
:顺序写。randread
:随机读。randwrite
:随机写。rw,readwrite
:混合顺序读写,读写占比默认各 50%。randrw
:混合随机读写,读写占比默认各 50%。
blocksize
=int[,int][,int],bs
=int[,int][,int]:I/O 单元的块大小,以字节为单位(默认 4096)。以下是几个示例(注意可以用 k、m、g 后缀,分别表示 1024、1024*1024、1024*1024*1024,不区分大小写):
bs=256k
:读、写和 trim 的块大小都是 256k 字节(注意 trim 概念这里并不涉及,后面将不再提及)。bs=8k,32k
:读的块大小为 8k 字节,写的块大小是 32k 字节。bs=,8k
:读的块大小为默认值(即 4096,4k 字节),写的块大小为 8k 字节。bs=8k,
:读的块大小为 8k 字节,写的块大小为默认值(即 4096,4k 字节)。
size
=int:指定了测试文件的大小(字节),可以使用具体的大小(比如 8G)以及区间范围(比如 10G-20G)。在使用具体大小的情况下,文件将被创建为指定大小。在使用区间范围的情况下,文件将被随机创建为分布在该范围内的不同大小。iodepth
=int:I/O 深度,用于异步 I/O(默认值为 1,同步 I/O 即便设置也无效),指每个线程并发 I/O 请求的数量,也可以理解为发出多少个 I/O 请求后才等待响应。例如,如果使用--iodepth=32
,每个线程就同时发送 32 个 I/O 请求,然后等待 I/O 响应,再发送另外 32 个 I/O 请求。具体来说,这意味着 fio 将在测试期间创建多个线程,每个线程负责模拟多个客户端并发地读写文件。fliename
=str:指定测试文件的文件名(含路径),与filename_format
二选一。参数size
指定了这个文件要读写的大小。directory
=str:指定测试文件的目录。fio 会把directory
和filename
进行拼接,得出最终的测试文件路径。filename_format
=format:与filename
二选一,这个支持在文件名中包含变量,变量名以$
开头。如果filename
和filename_format
都未设定,就会以$jobname.$jobnum.$filenum
为默认的文件名。支持的变量名有以下几个:
$jobname
:作业名。$clientuid
:IP of the fio process when using client/server mode.$jobnum
:The incremental number of the worker thread or process.$filenum
:The incremental number of the file for that worker thread or process.
thread
=int:fio 的并发线程总数,默认值为 1。仅用于异步 I/O,同步 I/O 配置了也无效,因为同步 I/O 只能顺序执行。numjobs
=int:fio 并发执行的 job 数量,默认值为 1。这里解释下参数 thread 和 numjobs 的区别。thread 指的是 fio 启动的线程总数,numjobs 指的是并发的 job 数。我们可以把 numjobs 理解为应用程序,有多少个应用程序在同时访问文件系统。例如 —thread=8,—numjobs=4, 可以理解为有 4 个应用程序在同时访问文件系统,每个应用程序里有 2 个线程。
-
上面这些参数,已经基本可以满足大部分 fio 测试场景了。不过这里还有一些我认为比较有用的,可以用于调试等的参数(仅可以用在命令行的参数有标注,都命令行与作业文件都可以用的,命令行里的属性名字要添加前缀 --
):
group_reporting
:控制 fio 的报告方式。默认情况下,每个作业的结果单独输出,你可以使用此选项将所有作业的结果合并输出(包含多个 job,以及 numjobs > 1 的情况)。此参数写在作业文件中的话,放在[global]
字段下对于所有 job 有效,否则只将设定了此属性的 job 有效。runtime
:最大的运行时间。作业将在执行完成,或者到达此参数设定的最大时间时停止。有些时候我们无法预估一个作业的执行时间,使用此参数可以让我们轻松限定作业执行的最大时间。time_based
:一般与--runtime
搭配使用。如果设置,即使文件被完全读取或写入,fio 也会在指定的--runtime
期间继续运行,它只会在运行时允许的情况下多次循环相同的工作负载。stonewall
:适用于一个作业文件内有多个测试 job 的场景,表示等待作业文件中的先前作业退出,然后再开始此作业,可用于在作业文件中插入序列化点。stonewall
石墙还意味着开始一个新的报告组 group_reporting。--output
:后接文件名,将 fio 的输出写入到指定文件中。此参数只能在命令行使用。--output-format
=format:指定输出信息的格式。此参数只能在命令行使用。有以下几种选择:normal
:默认值,就是默认的输出格式。minimal
:最小化输出,就是把所有测试数据挤在一坨,由分号;
隔开,可以看做是表格(CSV 那种)。--output-format=minimal
与直接在命令行使用--minmal
等价。terse
:与 minimal 基本相同,基于 CSV 格式,输出更详细一丁点,列出了更多指标(例如百分比延迟)。json
:以 json 的形式输出结果。json+
:与json
基本一样,只是添加了完整了延迟 buckets。
--eta
:作业执行时,在旁边显示预估的剩余时间。此参数只能在命令行使用。有三个值:auto
:自动选择,是默认值。always
:总是显示。never
:从不显示。
--debug
=type:显示更多的调试信息,可以设置多个项,每个项之间用逗号,
隔开(如--debug=file,mem
)。此参数只能在命令行使用。有以下取值(这里都比较简单,而且也不是特别常用,就不翻译了=。=):process
:Dump info related to processes.file
:Dump info related to file actions.io
:Dump info related to I/O queuing.mem
:Dump info related to memory allocations.blktrace
:Dump info related to blktrace setup.verify
:Dump info related to I/O verification.random
:Dump info related to random offset generation.parse
:Dump info related to option matching and parsing.diskutil
:Dump info related to disk utilization updates.job:x
:Dump info only related to job number x.mutex
:Dump info only related to mutex up/down ops.profile
:Dump info related to profile extensions.time
:Dump info related to internal time keeping.net
:Dump info related to networking connections.rate
:Dump info related to I/O rate switching.compress
:Dump info related to log compress/decompress.steadystate
:Dump info related to steadystate detection.helperthread
:Dump info related to the helper thread.zbd
:Dump info related to support for zoned block devices.all
:Enable all debug options.?
orhelp
:Show available debug options.
5. fio 测试存储系统的结果分析
我这里使用一个简单的、但包含了上面提到的所有常用属性的作业文件 jobfile.fio
(虽然包含了上面提到的全部参数,但是有些参数之间是有冲突的,下面分析输出的时候会说道),其内容如下:
1 | [global] |
我这里选择的是读写模式,这样可以尽可能的在输出中看到更多的内容,方便解释各个部分值的含义。
这些参数的含义上面都说过了,这里就不解释了。然后执行这个作业:
1 | fio jobfile.fio |
输出如下:
1 | my-example-job: (g=0): rw=rw, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=pvsync, iodepth=16 |
并且当前目录下会有两个 100MB 的文件(这是我们作业文件里配置的)。
1 | du -sh * |
有两个文件是因为我在参数 filename_format
中使用了 $jobnum
变量,并且参数 numjobs
是 2。$jobnum
变量将这两个 job 的输出文件名区分开了,所以会有两个。如果不使用 $jobnum
,那么应当就只有一个,因为两个 job 输出的文件名相同。
下面我们开始解释上面那一大坨输出!我们一行一行看 ~
-
第 1 行:
1 | my-example-job: (g=0): rw=rw, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=pvsync, iodepth=16 |
这里主要是执行作业的部分参数,分别有作业名字 job1,作业组 g
,bs
的值(R 为读、W 为写、T 为 Trim 但我们这里不涉及,就不解释了),还有 ioengine
和 iodepth
。这些参数的含义我们之前都解释过,这里就直接过了,很简单。
-
第 2 行:
1 | ... |
这个就是个分隔行,第 1 行是打印了这个作业的一些参数,从第 3 行开始就是真的作业测试输出了。
-
第 3 行:
1 | fio-3.35-2-g954b8 |
当前 fio 的版本号。
-
第 4 行:
1 | Starting 2 threads |
表示正在启动 2 个线程。
这里是由我们配置的 ioengine
、thread
参数和 numjobs
参数共同决定的。
当 ioengine
配置的是同步 I/O,thread
参数无效,不会被读取,实际启动的线程数等于 numjobs
,即个 job 有 1 个线程。
当 ioengine
配置的是异步 I/O,实际启动的线程数等于 thread
,numjobs
个 job 一共使用 thread
个线程。
在我们这个场景中,我们配置了同步 I/O ioengine=pvsync
,thread=5
和 numjobs=2
,所以这里 thread
参数是无效的,实际执行的线程数等于 numjobs
,即为 2。
-
第 5 - 6 行:
1 | my-example-job: Laying out IO file (1 file / 100MiB) |
每行指的是 job 1 正在生成一个 IO 文件,大小为 100MiB。这里有两行是因为我们配置了 numjobs=2
。
-
第 7 - 8 行:
1 | note: both iodepth >= 1 and synchronous I/O engine are selected, queue depth will be capped at 1 |
这是一个错误信息,因为我上面配置了使用同步 I/O(pvsync)又配置了 iodepth
,而 iodepth
参数仅对异步 I/O 有效,这里是告诉我们 iodepth
会被设置为最大为 1。
这里有两行是因为我们配置了 numjobs=2
,每个 job 都输出了一次。
-
第 9 行:
1 | Jobs: 2 (f=2): [M(2)][85.7%][r=14.4MiB/s,w=14.0MiB/s][r=1840,w=1796 IOPS][eta 00m:01s] |
这行信息是进度条,是实时变化的,其中含义如下:
Jobs: 2 (f=2)
:对测试的工作线程数量进行的汇报。在这个例子中,fio 启动了 2 个工作线程(job)。f=2
表示numjobs
的值是 2,Jobs
的值和f
的值相同代表着线程数与设定的相同。有这项输出是因为 fio 并不一定会按照设定参数启动线程,fio 可以自动调整工作线程的数量以适应测试负载和 I/O 模式。[M(2)][85.7%]
:表示当前测试的状态和进度百分比。[M(2)]
表示当前测试正在进行中,总共有 2 个工作线程(job)。[85.7%]
表示测试已经完成的百分比,这个值最终应当为 100%,但是由于我这里写入的文件很小,还没来得及更新这个值呢程序就结束了,输出了下面那些行的信息。[r=14.4MiB/s,w=14.0MiB/s]
:表示读取(r)和写入(w)速度的统计信息。在这个例子中,读取速度为 14.4 MiB/s,写入速度为 14.0 MiB/s。[r=1840,w=1796 IOPS]
:表示每秒读取(r)和写入(w)的 I/O 操作数量统计信息。在这个例子中,读取速度为 1840 IOPS,写入速度为 1796 IOPS。[eta 00m:01s]
:表示预估的剩余测试时间(estimated time of arrival)。在这个例子中,测试的剩余时间预计为 00 分钟 01 秒,即测试应该在 1 秒内完成。
注意上述信息是在 fio 测试进行时实时变化的,并不是最终的测试结果信息。
-
第 10 行:
1 | my-example-job: (groupid=0, jobs=2): err= 0: pid=504766: Sun May 28 14:46:42 2023 |
这行很简单,分别是作业名 my-example-job
,组 id groupid=0
,作业数 jobs=2
,错误数 err= 0
,进程 pid=504766
,和执行此测试的时间 Sun May 28 14:46:42 2023
。
-
第 11 - 21 行:
1 | read: IOPS=2120, BW=16.6MiB/s (17.4MB/s)(99.2MiB/5987msec) |
这些都是读操作的测试结果。
这里的第一行是总体概括的信息,有以下内容:
IOPS=2120
:表示 IO 操作每秒的数量,即 I/O Operations Per Second,单位为 次/秒。BW=16.6MiB/s(17.4MB/s)
:表示 IO 操作的带宽,即 Bandwidth,也就是数据传输速度,以 MiB/s 或 MB/s 为单位。(99.2MiB/5987msec)
:表示本次读取操作传输的数据量和所花费的时间,其中数据量为 99.2MiB,时间为 5987 毫秒。
然后下面各项分别是(单位都是 usec,即微秒):
clat
指标是“完成时间”(Completion Latency)的缩写,代表的是 IO 操作完成所花费的时间,不包括 IO 队列等待时间。lat
指标是“延迟”(Latency)的缩写,代表的是 IO 操作完成所花费的总时间,包括 IO 队列等待时间和完成时间。clat
通常用于衡量存储设备的性能,因为设备的响应速度是关键因素。而lat
则更加适用于衡量整个系统的性能,因为它考虑了所有相关因素。clat
指标所显示的数值比lat
要小,因为它不包括 IO 队列等待时间。但在由于本例中使用的是同步 IO,不需要等待 I/O 队列,所以clat
和lat
的值基本一样。clat percentiles
:一组clat
的百分位数的统计指标,用于显示I/O操作完成时间延迟(Completion Latency)的不同分布情况。bw
: 带宽。iops
:这个没啥好说的,就是每秒的 IO 次数。
然后具体到每个项,还有一些细分的值:
min
:最小值。max
:最大值。avg
:平均值。stdev
:标准差。per
:使用的百分数。bw
中为 100.00% 表示 100% 的带宽分布在结果中。samples
:表示测试期间完成 I/O 操作的次数,即测试的样本数量。这里的 22 表示整个测试过程完成的完整 I/O 操作次数为 22 次。
-
第 22 - 32 行:
1 | write: IOPS=2155, BW=16.8MiB/s (17.7MB/s)(101MiB/5987msec); 0 zone resets |
这些都是写操作的测试结果,各个项的含义与上面说过的读操作的一一对应,就不再重复解释了。
-
第 33 - 34 行:
1 | lat (usec) : 100=17.83%, 250=74.61%, 500=2.43%, 750=0.44%, 1000=0.20% |
第一行中的 lat (usec)
表示延迟时间的单位为微秒(usec),后面的数字表示各个延迟时间区间所占的比例。例如,上述输出结果中的 100=17.83%, 250=74.61%, 500=2.43%
表示完成时间延迟在 100 微秒以下的 I/O 操作有 17.83%;延迟在 100~250 微秒之间的操作有 74.61%,延迟在 250~500 微秒之间的操作有 2.43%。
第二行中 lat (msec)
表示延迟时间的单位为毫秒(msec),后面的数字表示不同的延迟时间区间所占的比例。例如,上述输出结果中的2=0.12%, 4=0.04%, 10=4.34%”
表示完成时间延迟在 2 毫秒以下的 I/O 操作有 0.12%;延迟在 2 ~ 4 毫秒之间的操作有 0.04%;延迟在 4 毫秒以上但 10 毫秒以下的操作有 4.34%。
这些数据可以帮助我们了解 I/O 操作的完成时间延迟的分布情况,确定延迟时间的一些关键点,如 99th 百分位数或平均延迟时间等,以评估存储设备或系统的性能是否符合预期,并进行性能优化。
-
第 35 行:
1 | cpu : usr=0.49%, sys=1.18%, ctx=25603, majf=0, minf=0 |
在fio测试输出结果中,这一行是用来描述测试过程中 CPU 使用情况的,其中:
usr
表示用户 CPU 时间的占比。在这里,”usr=0.49%” 表示完成 I/O 操作的过程中,用户态 CPU 总共使用了 0.49% 的时间。sys
表示内核 CPU 时间的占比。在这里,”sys=1.18%” 表示完成 I/O 操作的过程中,内核态 CPU 总共使用了 1.18% 的时间。ctx
表示上下文切换的次数。在这里,”ctx=25603” 表示完成 I/O 操作的过程中,操作系统的上下文切换次数共计 25603 次。majf
表示发生的主要页故障的数量,当进程访问不在主存中的页时需要主要页故障进行处理。在这里,”majf=0” 表示完成 I/O 操作的过程中,没有发生主要页故障。minf
表示发生的次要页故障的数量,它是当操作系统访问到磁盘上的虚拟内存页时发生的。在这里,”minf=0” 表示完成 I/O 操作的过程中,没有发生次要页故障。
这些数据可以帮助我们评估测试环境中 CPU 的使用情况,以及系统的稳定性。在进行性能测试时,需要考虑测试过程中的各种因素,例如 CPU、磁盘、内存等的负载情况,并根据需要进行相应的调整,以获得更加准确的测试结果。
-
第 36 - 40 行:
1 | IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% |
这段输出描述了 I/O 操作深度和 I/O 请求的分布情况。
depths
列出了请求队列中处理 I/O 请求时所使用的深度。例如,depths
中的 “1=100.0%”,表示所有的 I/O 请求都是深度为 1 的。submit
表示针对不同深度的 I/O 请求的数量。例如,submit
中的 “4=100.0%” 表示所有的 I/O 请求的深度都是 4。当 fio 运行时,会创建一个 I/O 请求队列,其中包含了待处理的 I/O 请求。队列中的 I/O 请求按照深度级别(iodepth level)分组,通常每个深度级别都有一定数量的 I/O 请求。
submit
则表示在 I/O 请求队列中最大的同时启动的请求数量,也就是说,每次最多启动多少个 I/O 请求同时加入后端处理队列。其值由 fio 的内部算法决定,具体取决于 I/O 操作的类型、深度、数据文件大小等因素。complete
表示已完成处理并通过返回数据来完成读操作或写操作的请求数。在这个示例中,complete
中的 “4=100.0%” 表示所有的请求都已经被完成处理。issued rwts
里 “total” 的值表示总共发出了 12697 个读、12903 个写请求。”short” 表示长度非常短的请求,可能是被操作系统合并或去重后的结果。”dropped” 表示在处理的时候,不需要实际发出的I/O 请求数量,这里的数量为 0。latency
列出了测试的延迟,包括目标、窗口和百分位数、深度信息。target
表示测试设置的延迟目标。在这里,目标被设置为 0,即最小化延迟。window
表示在运行测试期间,记录延迟的时间窗口大小。在这里,窗口大小被设置为 0,即记录所有延迟时间。percentile
是指多少百分比的 I/O 操作完成时间在某个特定时间内。在这里,percentile 被设置为 100%,即记录所有 I/O 操作的完成时间。depth
列出了要记录的最大深度,即要记录最大深度为 16 的 I/O 操作延迟时间。
-
第 42 - 44 行:
1 | Run status group 0 (all jobs): |
这段输出描述了 fio 测试的运行状态和结果:
- “Run status group 0 (all jobs)” 表示所有测试任务组的状态和结果,因为只有一个组 0,所有任务都在这个组里。
READ
和WRITE
分别表示读和写的测试任务组。bw
表示每秒的传输速率(带宽),单位是MiB/s(MB/s)。io
表示总的数据传输量,单位是 MiB(MB)。run
表示测试的运行时间,单位是 msec(毫秒)。
在括号中的数字表示速率或持续时间的范围,例如 “16.6MiB/s-16.6MiB/s” 表示测试期间传输速率保持稳定。
-
第 46 - 47 行:
1 | Disk stats (read/write): |
这段输出描述了磁盘的统计信息,包括读取和写入操作的 I/O 个数、合并操作的次数、处理 I/O 的时间(以 ticks 表示)、等待处理的 I/O 个数以及磁盘的利用率。
“vda” 表示磁盘的名称。可以用
fdisk
命令查看我们的磁盘名称,例如我这里磁盘名称就是 “vda”:1
2
3sudo fdisk -l
Disk /dev/vda: 60 GiB, 64424509440 bytes, 125829120 sectors
...
ios
表示读取和写入操作的 I/O 个数。对于此示例,读取操作是 12320,写入操作是 12573。merge
表示合并操作的次数,在此示例中为 0。ticks
表示处理每个 I/O 请求所需的时间,以毫秒为单位。在此示例中,读操作处理时间为 5326 毫秒,写操作为 6099 毫秒。in_queue
表示等待处理的 I/O 数量。在此示例中,等待处理的 I/O 数量为 11425。util
表示磁盘的利用率。在此示例中,磁盘利用率为 98.35%,表明磁盘的读取和写入吞吐量很高,几乎处于满负荷状态。
6. 附录:常用的作业文件模板
这里给出一些常用的,fio 测试文件系统性能的作业文件。
我这里同步 I/O 使用 pvsync,异步 I/O 使用 io_uring。
6.1. 同步 I/O
1 | [global] |
6.2. 异步 I/O
1 | [global] |
6.3. 扩展:使用 fio 创建指定数量的文件
这个场景其实和本文主题关系不大,但是是我在实际工作中会用到的,就是直接往文件系统里写数据。
作业文件如下:
1 | [create-files] |
主要的意思就是启动 numjobs
个任务,每个任务在目录 directory
下写入 nrfiles
个文件,每个文件大小为 filesize
,总共写入 numjobs
* nrfiles
个文件。
例如,上述作业文件,就是启动 2 个任务,每个任务在目录 /mnt/data/ 下写入 100 个文件,每个文件大小为 1MB,总共写入 200 个文件。
不过这个就是偶尔用用,而且 fio 不太好删这些创建出来的文件,我一般都手动删除。
如果常用关于大量文件的创建、删除、修改等元数据操作的,可能其他工具更合适,例如 mdtest。