JasonWang's Blog

如何移植fio到Android平台

字数统计: 1.8k阅读时长: 9 min
2024/03/28

fio是一个广泛使用的磁盘性能测试工具,功能强大,可以用于测试磁盘性能,也可以通过I/O重放来模拟用户的实际请求,其主要有如下几个特点:

  • 支持多种文件系统,包括NTFS,ext4,btrfs,xfs等
  • 支持多种IO模式,包括randwrite,read,write,dd,trim,flush,discard等
  • fio可以测试不同类型的IO,包括随机写,连续写,顺序写,随机读,连续读,顺序读等
  • 另外fio还支持I/O限制,可以限制IO带宽,IO延迟,IO吞吐量等

这篇文章,我们将介绍如何移植fio到Android平台,以及常见的使用方法。首先来看看如何通过交叉编译移植fio

编译准备

首先到fio的官方网站https://github.com/axboe/fio下载源码:

1
2
3

git clone https://github.com/axboe/fio

如果本地没有安装过NDK,需要下载NDK:https://developer.android.google.cn/ndk/downloads?hl=zh-cn;然后设定NDK的环境变量:

1
2
3

export NDK_HOME=/path/to/ndk

编译代码

在编译之前,先通过./configure --help看看编译配置具体有哪些参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
./configure --help
--prefix= Use this directory as installation prefix
--cpu= Specify target CPU if auto-detect fails
--cc= Specify compiler to use
--extra-cflags= Specify extra CFLAGS to pass to compiler
--build-32bit-win Enable 32-bit build on Windows
--target-win-ver= Minimum version of Windows to target (only accepts 7)
--enable-pdb Enable Windows PDB symbols generation (needs clang/lld)
--build-static Build a static fio
--esx Configure build options for esx
--enable-gfio Enable building of gtk gfio
--disable-numa Disable libnuma even if found
--disable-rdma Disable RDMA support even if found
--disable-rados Disable Rados support even if found
--disable-rbd Disable Rados Block Device even if found
--disable-http Disable HTTP support even if found
--disable-gfapi Disable gfapi
--enable-libhdfs Enable hdfs support
--enable-libnfs Enable nfs support
--disable-libnfs Disable nfs support
--disable-lex Disable use of lex/yacc for math
--disable-pmem Disable pmem based engines even if found
--enable-lex Enable use of lex/yacc for math
--disable-shm Disable SHM support
--disable-optimizations Don't enable compiler optimizations
--enable-cuda Enable GPUDirect RDMA support
--enable-libcufile Enable GPUDirect Storage cuFile support
--disable-native Don't build for native host
--with-ime= Install path for DDN's Infinite Memory Engine
--enable-libiscsi Enable iscsi support
--enable-libnbd Enable libnbd (NBD engine) support
--disable-xnvme Disable xnvme support even if found
--disable-isal Disable isal support even if found
--disable-libblkio Disable libblkio support even if found
--disable-libzbc Disable libzbc even if found
--disable-tcmalloc Disable tcmalloc support
--dynamic-libengines Lib-based ioengines as dynamic libraries
--disable-dfs Disable DAOS File System support even if found
--enable-asan Enable address sanitizer
--seed-buckets= Number of seed buckets for the refill-buffer
--disable-tls Disable __thread local storage

具体需要哪些选项,我们可以根据需要来进行选择与配置。为了便于编译,我们写一个简单的编译脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

UNAME=Android
ARCH=arm64
CPU=aarch64
API=26
PREFIX=$(pwd)/Android/$CPU
CROSS_COMPILE=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android$API-
CC=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android$API-clang
#CROSS_PREFIX=$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android$API-

./configure --prefix=$PREFIX --cpu=$CPU --cc=$CC --build-static --disable-numa

make clean
make V=1 UNAME=$UNAME CROSS_COMPILE=$CROSS_COMPILE

执行上述脚本发现没在配置阶段会提示错误: 交叉编译工具使用的是 gcc, 而看NDK的工具都是 clang 的,需要修改下配置:

  • 由于当前NDK都采用 clang, 但 fio源码配置文件默认是 gcc的编译器,因此需要修改下 configure 文件:
1
2
3
4
5
6
7
8
9
10
11
diff --git a/configure b/configure
index 420d97db..c245bcd9 100755
--- a/configure
+++ b/configure
@@ -350,7 +350,7 @@ if test -z "${CC}${cross_prefix}"; then
cc=clang
fi
else
- cc="${CC-${cross_prefix}gcc}"
+ cc="${CC-${cross_prefix}clang}"
fi

修改完后,再次编译还是会提示错误:

1
2
3
4
5
6
7
8
9
In file included from engines/io_uring.c:29:
engines/nvme.h:18:8: error: redefinition of 'nvme_uring_cmd'
struct nvme_uring_cmd {
^
/home/jason/Android/Sdk/ndk/26.1.10909125/toolchains/llvm/prebuilt/linux-x86_64/bin/../sysroot/usr/include/linux/nvme_ioctl.h:80:8: note: previous definition is here
struct nvme_uring_cmd {
^
1 error generated.
make: *** [Makefile:526: engines/io_uring.o] Error 1

看代码是由于重复定义导致了, fio的源码里有一个地方重新定义了, 因此需要针对Android平台做判断, 照例修改下 configure的配置即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
* If the uapi headers installed on the system lacks nvme uring command
* support, use the local version to prevent compilation issues.
*/
#ifndef CONFIG_NVME_URING_CMD
struct nvme_uring_cmd {
__u8 opcode;
__u8 flags;
__u16 rsvd1;
__u32 nsid;
__u32 cdw2;
__u32 cdw3;
__u64 metadata;
__u64 addr;
__u32 metadata_len;
__u32 data_len;
__u32 cdw10;
__u32 cdw11;
__u32 cdw12;
__u32 cdw13;
__u32 cdw14;
__u32 cdw15;
__u32 timeout_ms;
__u32 rsvd2;
};

找到生成 CONFIG_NVME_URING_CMD的地方,增加对 Android的判断即可:

1
2
3
4
5
6
7
8
9
10
@@ -2656,7 +2656,7 @@ if test "$libzbc" != "no" ; then
fi
print_config "libzbc engine" "$libzbc"

-if test "$targetos" = "Linux" ; then
+if test "$targetos" = "Linux" || test "$targetos" = "Android"; then
##########################################
# Check NVME_URING_CMD support
cat > $TMPC << EOF

这一次编译正常了,可以看到有生成可执行文件,将其push到Android设备,可以正常执行。

如何使用fio

fio的命令主要有两个部分,一部分是参数,一部分是测试的配置文件。

1
2
3

fio [options] [jobfile] ...

参数用于设置fio以及输出一些调试信息,而真正让fio运行的是jobfile,用于指定运行时的测试参数,包括如下几个部分:

  • I/O的类型: 指定读写模式,比如是顺序读还是随机读等,比如是否使用直接I/O(direct I/O)
  • 读写块大小: 指定读写块大小,比如是4k还是64k
  • 读写的总文件大小: 指定文件大小,比如是1G还是10G
  • I/O引擎: 使用共享内存的方式还是普通的读写操作
  • I/O深度: 对于使用异步I/O引擎的情况,指定I/O队列的大小
  • 目标文件与设备: 指定测试需要执行的文件或者设备
  • 线程或进程: 指定测试需要执行的线程数量

比如如果我们需要测试某个磁盘的性能可以使用直接模式,具体命令如下:

1
2
3

fio -direct=1 -iodepth=1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=10G -numjobs=10 -runtime=1000 -group_reporting -name=fio-test

测试完成后,fio会生成报告, 包括I/O带宽,读写的速度以及I/O延迟等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Jobs: 10 (f=10): [w(10)][85.7%][w=1829MiB/s][w=117k IOPS][eta 00m:01s]
fio-test: (groupid=0, jobs=10): err= 0: pid=16991: Sun Mar 31 18:03:08 2024
write: IOPS=95.3k, BW=1489MiB/s (1562MB/s)(10.0GiB/6875msec); 0 zone resets
clat (usec): min=14, max=545565, avg=101.30, stdev=3043.89
lat (usec): min=14, max=545569, avg=103.13, stdev=3043.92
clat percentiles (usec):
| 1.00th=[ 24], 5.00th=[ 28], 10.00th=[ 32], 20.00th=[ 38],
| 30.00th=[ 45], 40.00th=[ 53], 50.00th=[ 58], 60.00th=[ 61],
| 70.00th=[ 64], 80.00th=[ 68], 90.00th=[ 74], 95.00th=[ 86],
| 99.00th=[ 668], 99.50th=[ 2278], 99.90th=[ 4817], 99.95th=[ 5342],
| 99.99th=[ 6587]
bw ( MiB/s): min= 0, max= 1947, per=98.94%, avg=1473.64, stdev=65.83, samples=130
iops : min= 20, max=124616, avg=94312.77, stdev=4213.24, samples=130
lat (usec) : 20=0.12%, 50=35.96%, 100=60.59%, 250=2.20%, 500=0.09%
lat (usec) : 750=0.06%, 1000=0.16%
lat (msec) : 2=0.20%, 4=0.40%, 10=0.22%, 20=0.01%, 250=0.01%
lat (msec) : 750=0.01%
cpu : usr=4.98%, sys=37.83%, ctx=664271, majf=0, minf=0
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,655360,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
WRITE: bw=1489MiB/s (1562MB/s), 1489MiB/s-1489MiB/s (1562MB/s-1562MB/s), io=10.0GiB (10.7GB), run=6875-6875msec

Disk stats (read/write):
nvme0n1: ios=0/628636, sectors=0/20133840, merge=0/2245, ticks=0/40603, in_queue=41130, util=96.51%

参考资料

原文作者:Jason Wang

更新日期:2024-03-31, 18:06:11

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 编译准备
  2. 2. 编译代码
  3. 3. 如何使用fio
  4. 4. 参考资料