存储性能SPDK调优指导手册




SPDK 是干嘛的 ? 好处是啥 ?

# SPDK 是一个由 Intel 发起的, 用于加速 NVME SSD 作为后端存储使用的应用软件加速库(存储性能开发工具包);
    这个软件库的核心是用户态、异步、轮询方式的 NVME 驱动,相比内核 NVME 驱动,SPDK 可以大幅降低 NVME command 的延迟,提高单 CPU 核的 IOPS, 形成一套高性价比的解决方案,如 SPDK 的 vhost 解决方案可以被应用到 HCI 中加速虚拟机的 NVME I/O。

    SPDK 提供了一整套工具和库,用于编写高性能、可扩展的用户模式存储应用程序。它通过使用如下许多关键技术来实现其高性能:
    1). 内核旁路(kernel bypass)技术: 也就是数据绕过了内核,避免了系统调用和上下文切换的时间开销
    2). 零拷贝技术: 通过自定义的用户态 NVME 驱动和其他一些代替内核协议栈的工具集,直接省去了数据在主机内存的用户空间和内核空间复制的频繁臃肿操作,从而极大的降低了数据收发过程的时延
    3). 无锁技术: 我们都知道线程,进程间的通信以及系统访问大量的硬件(NIC, FC , NVME , GPU, USB等)  的过程中都用到了大量的锁的机制,而这也不可避免的给系统资源带来了损耗,故为了避免 Nvme 数据收发 I/O 路径中的锁的运用,SPDK   Of  Nvme 驱动则是使用了 用户态的消息传递机制提升了系统下的资源利用率;
    4). 大页技术: 即通过内存池技术,极大的提升了内存使用效率和降低了管理内存的消耗;其具体表现则是1. 缩小了 TLB 条目 ,从而提高了TLB的命中率; 2 . 减少了页表级数,也可以减少查找页表的时间; 3. 减少缺页异常(page fault)的发生次数
    5). 异步轮询技术: 大家都知道Nvme传统数据收发过程需要中断的深度参与来提醒,而中断处理程序又是一个极大消耗CPU时钟的费时程序,故通过改变传统中断的方式为用户态下的数据轮询操作来降低对 CPU 资源的消耗,从而也降低了数据收发的时延;
    6). CPU 亲和技术:即频繁的上下文切换消耗了珍贵的 CPU 时钟和资源; 通过 CPU 隔离再绑定,极大的提升了 nvme 驱动和相关协议栈处理数据的效率;

    SPDK 还提供了一个完整的块堆栈作为用户空间库,该库执行许多与操作系统中的块堆栈相同的操作。这包括统一不同存储设备之间的接口、排队处理内存不足或 I/O 挂起等情况;
    性能的影响主要在于硬件设备(包括 fireware)和软件的开销。 从持续满足上层应用的高性能的角度看,有两种途径: 一是开发更高性能的固态硬盘硬件设备;二是减少软件的开销。目前,这的确是两条在并行前进的道路。在硬件方面,基于最新的 3D XPoint 技术和 Intel Optane NVME SSD 设备可以在延迟和吞吐量方面使得性能更上一层楼。然而在软件方面,同样也是有层出不穷的技术在不断涌现,这其中颇为受到青睐的就属 SPDK 了; 它的出现从整体上提高了上层应用对设备的访问性能,而它的核心组件之一则就是用户态的 NVME 驱动。
    在传统模式下,设备驱动和各个协议栈都存在于内核态下的内核当中,当内核驱动模块在内核中加载成功后,会被标识为块设备还是字符设备,同时定义相关的访问接口,包括管理控制接口、数据接口等,这些接口直接的或间接的与文件系统子系统相结合,暴露给用户态中的应用程序,而后当这些应用程序想要操作硬件设备时,则就会通过系统调用的方式通过内核驱动对其发起控制和读/写操作。
    用户态应用程序和内核驱动的交互离不开用户态和内核态的上下文切换,以及系统调用的开销。然而随着社会的发展,对应用程序的性能要求越来越高,包括吞吐量和时延,但又因为频繁的上下文切换和系统调用极大的浪费了 CPU 计算资源和提高了数据传输过程中的整体时延。这时就不得不想办法避免频繁的上下文切换和系统调用,至此,用户态的驱动就出现了,它的一经问世,立马就极大的减少了软件本身的开销,包括前面提及的上下文切换和系统调用,因为它的存在,当应用程序再想操作硬件设备时,则就完全无需再频繁的和内核态中的程序打交道了,直接与用户态的驱动交互,也就相当于直接绕过了内核,同时数据的通路也就绕过了内核,随之也理所当然的避免了频繁的上下文切换和系统调用所造成的时间开销和资源浪费,同时也省去了数据在主机内存的用户空间和内核空间之间复制的无用操作步骤。
    当前在用户态,可以通过 UIO(Userspace I/O) 或 VFIO(Virtual Function I/O)两种方式对硬件设备进行访问。

安装前准备工作

#1. 本地 Everything 源的配置 
#2. 本机若没联网,则还需配置网络代理
export http_proxy=http://sitos:sitos@172.16.1.11:808
export https_proxy=http://sitos:sitos@172.16.1.11:808
#3. 网络源的挂载 
#4. 本机若没联网,则 Git 还需配置网络代理
git config --global http.proxy http://sitos:sitos@172.16.1.11:808
git config --global https.proxy http://sitos:sitos@172.16.1.11:808
#5. 为确保安装成功,Pip 还需配置国内安装源,配置代码如下:
mkdir ~/.pip 
touch ~/.pip/pip.conf 
cat > ~/.pip/pip.conf << EOF 
[global]
trusted-host = mirrors.aliyun.com
index-url = http://mirrors.aliyun.com/pypi/simple
EOF
#6. 为确保 Git 从 Github 下载源码包可以成功, Git 还需做如下配置 
git config --global http.sslVerify false
git config --global http.postBuffer 52428800000   # 设置增大 Git 缓冲区大小, 必须的
git config --global http.lowSpeedTime  999999
git config --global http.lowSpeedLimit 0
export GIT_TRACE_PACKET=1
export GIT_TRACE=1
export GIT_CURL_VERBOSE=1

依赖包安装

	SPDK 所依赖的很多依赖包在本地 IOS 镜像中不存在,需挂载该系统的 Everything ISO 本地镜像源或挂载网络镜像源;
# 安装 001 
yum install -y gcc gcc-c++ make CUnit-devel libaio-devel openssl-devel \
	libuuid-devel libiscsi-devel ncurses-devel json-c-devel libcmocka-devel \
	clang clang-devel python3-pip numactl-devel patchelf
# 安装 002
yum install -y autoconf automake libtool help2man make nasm systemtap-sdt-devel
# 安装 003 -- RDMA 
yum install -y libibverbs-devel librdmacm-devel
# 安装 004 -- Perl
yum install -y perl 
# 安装 005 -- Python , 若系统下已安装 python3 ,则可忽略该安装 
yum install -y python python3-devel

# 安装 python 依赖包 -- 若系统未安装 PIP,则需到 PYPI 下载 PIP 源码包进行安装 
pip3 install pyelftools  
pip3 install ninja  # 该包也可用 Yum 源安装 
pip3 install meson  # 该包也可用 Yum 源安装 
pip3 install flask pyyaml Jinja2
pip3 install ijson
pip3 install python-magic scikit-build grpcio grpcio-tools 
pip3 install paramiko pexpect pandas tabulate configshell-fb pyparsing
	# 注意:以上依赖包的记录是基于本人在 OpenEular 系统上安装 SPDK 所总结的经验,但大差不差,若是其他的系统,可能会有一些其他的特殊依赖包,故需安装 SPDK 时需重点关注所打印的日志;

	# 为了简化依赖包安装的操作度或者是增加 SPDK 的易用性,SPDK的源码包特意为我们提供了不同类型系统安装依赖的脚本:这些脚本的存放路径为: scripts/pkgdep ;脚本的名称分别以系统发行版名称命名,使用如下命令获取:
    source  /etc/os-release ; echo $ID
	# 但要特别注意的一点是如果你的系统是深度自定制的, 在通过以上命令获取系统发行版名称然后再判断所要执行的依赖包安装脚本时,若要发现不存在,这时可直接执行该定制系统在定制时所依赖的操作系统的依赖安装脚本;例如: 
    本人现在使用的系统是 电信内部自用的操作系统 CTYunOs-23.01.2, 系统名称为 ctyunos, 而 SPDK 未提供 ctyunos.sh 这个依赖安装脚本,但又因为这个系统是基于 OpenEuler 系统做的深度自定制,故我们这里可以直接将 SPDK 提供的 openeuler.sh 脚本改名为 ctyunos.sh, 然后执行安装依赖包即可,其他剑走偏锋的系统也是同理;
# 截止到本人使用时,SPDK 自动安装依赖包可支持的系统发行版有如下几个 
        arch.sh
        centos.sh
        common.sh
        debian.sh
        fedora.sh
        freebsd.sh
        openeuler.sh
        requirements.txt   # python 环境安装包管理文件:  pip3  install -r <>
        rhel.sh
        sles.sh
        ubuntu.sh

下载编译与安装 FIO

	由于本人使用 SPDK 软件开发平面集环境主要进行 Fio 性能测试,故还需将 SPDK 与 FIO 深度关联,这里就需要通过源码编译安装 FIO, 然后将 FIO 的源码安装包路径给到 SPDK, 然后在 SPDK 编译时通过指定该参数就可达到与 FIO 深度关联的目的; 源码编译安装 Fio 很简单,通过如下步骤依次执行即可;
# Fio 源码下载
git clone https://github.com/axboe/fio.git
# 编译安装 Fio
cd fio              # 进入源码包目录
chmod -R 777 ./*  
./configure
make && make install 

下载编译与安装 SPDK

Github --- SPDK 官网维护地址

	FIO 源码编译安装好之后,则就可以开始下载 SPDK 源码编译安装了,
# SPDK 源码下载 -- 因为 spdk 源码还依赖很多个子模块,故在下载时一定要添加 --recursive 参数;
git clone https://github.com/spdk/spdk --recursive 
# 编译安装 SPDK 
cd spdk                    # 进入源码包目录
chmod -R 777 ./* 
./configure --with-fio=/suossuo/spdk-home/fio/  # 指定 fio 源码安装路径 
make -j 6 && make install 

---------------------------------------------------------------
	编译 SPDK 完成后,可以查看SPDK下"/build/fio/"目录,里面是否生成 spdk_bdev 和 spdk_nvme 两个二进制文件。spdk_nvme 是基于裸盘NVMe的fio_plugin,其特点为I/O通过SPDK用户态驱动直接访问裸盘,常用于评估SPDK用户态驱动在裸盘上的性能。spdk_bdev 是基于bdev的fio_plugin,其特点为I/O测试基于SPDK块设备bdev之上,所有I/O经由块设备层bdev,再传送至裸盘设备。常用于评估SPDK块设备bdev的性能。
    默认情况下 SPDK 编译时不启用 RDMA 支持(因此不启用基于结构的 NVMe)。您可以通过执行以下操作来启用它:
    ./configure --with-rdma && make

SPDK 编译安装截图如下:

执行测试

测试前准备工作

IOMMU 配置

    许多平台都包含一个额外的硬件,称为 I/O 内存管理单元 (IOMMU)。IOMMU 与常规 MMU 非常相似,只是它为外围设备(即在 PCI 总线上的设备) 提供虚拟化地址空间。 MMU 知道系统上每个进程的虚拟到物理映射,因此 IOMMU 将特定设备与其中一个映射相关联,然后允许用户将任意总线地址分配给其进程中的虚拟地址。然后,通过IOMMU将总线地址转换为虚拟地址,然后将虚拟地址转换为物理地址,从而通过IOMMU转换PCI设备和系统内存之间的所有DMA操作。这允许操作系统自由修改虚拟到物理地址的映射,而不会中断正在进行的 DMA 操作。Linux 提供了一个设备驱动程序, vfio-pci 允许用户使用其当前进程配置 IOMMU。
    这是一个面向未来的硬件加速解决方案,用于在用户空间进程中执行 DMA 操作,并为 SPDK 和 DPDK 的内存管理策略奠定了长期基础。我们强烈建议使用 vfio 部署应用程序并启用 IOMMU,目前完全支持 IOMMU

    
    SPDK 默认情况下加载使用 uio_pci_generic 内核驱动
    IOMMU 可能存在于许多平台上并启用。当 IOMMU 存在并启用时, SPDK 预配置脚本 scripts/setup.sh 将自动选择加载 vfio-pci 驱动;
    但是,某些设备在绑定到 vfio-pci uio_pci_generic 内核驱动程序时可能无法正常工作,而是必须只有在加载内核驱动时才能正常工作。在这种情况下,用户应注意在运行 scripts/setup.sh 之前禁用 IOMMU 或将其设置为直通模式;
# 开启 IOMMU 的配置 -- /etc/default/grub 
在 GRUB 命令行参数最后添加如下配置: 
    intel_iommu=on iommu=pt # Intel  机器
    amd_iommu=on iommu=pt   # amd  机器
# 禁用 IOMMU 的方法 
在 GRUB 命令行参数最后添加如下配置: 
    intel_iommu=off   # intel CPU  
    amd_iommu=off     # amd 机器
# 配置 IOMMU 为直通模式 
	iommu.passthrough=1

    在某些情况下,用户可能不想使用 uio_pci_generic ,或者他们使用的内核版本存在 uio_pci_generic 无法绑定到 NVMe 驱动器的错误。在这种情况下,用户可以构建 igb_uio 内核模块,该模块可以在 dpdk-kmods 存储库中找到。为确保正确绑定驱动程序,用户应指定该环境变量: DRIVER_OVERRIDE=/path/to/igb_uio.ko ; 
# 重新编译 GRUB 使其新配置生效 
grub2-mkconfig  -o /boot/efi/EFI/ctyunos/grub.cfg  # UEFI   模式启动
grub2-mkconfig -o /boot/grub2/grub.cfg             # Legacy 模式启动

分配大页

	SPDK 依靠 DPDK 来分配固定内存。在 Linux 上,DPDK 通过分配 hugepages (默认为 2MiB)来实现此目的。Linux 内核处理大页面的方式与常规的 4KiB 页面不同。具体来说,操作系统永远不会更改其物理位置。
    机器系统下默认生效的内存大页页大小为 2MB,故 SPDK 内置 setup.sh 脚本配置的大页页大小也是 2 MB 的,且默认数量为 512 个页数;
# setup.sh 中用于指定内存大页页大小的环境变量;以kB为单位,如果未设置,则为内核默认值
HUGEPGSZ="2048"
# setup.sh 中用于指定总的大页内存大小的环境变量
HUGEMEM="1024"  # 默认为 1G,若页大小为 2MB, 则页数为 512 个; 
# setup.sh 中用于指定内存大页在各个 node 中均匀分布的环境变量 
HUGE_EVEN_ALLOC="yes"


要分配的大页面内存大小(以MB为单位)。默认为2048。
对于NUMA系统,巨大的页面将分布在node0上
默认的

# 在 GRUB 下配置内核参数开启 1G 内存大页页大小
default_hugepagesz=1G hugepagesz=1G hugepages=30
# 系统下手动配置 内存大页的 路径地址
/sys/kernel/mm/hugepages
/sys/devices/system/node/node0/hugepages
# 显示当前系统内存大页页数 
sysctl vm.nr_hugepages


# 创建大页装载
 mount -t hugetlbfs \
	-o uid=spdk,pagesize=<value>,size=<value>,\
	min_size=<value>,nr_inodes=<value> none /mnt/huge
mkdir /mnt/huge
mount -t hugetlbfs none /mnt/huge
# 查看大页挂载信息 
mount | grep huge

系统下成功分配 2 MB 大页截图如下:

系统下成功分配 1GB 大页截图如下:

解除 SPDK 内存限制

	一旦第一个设备连接到 SPDK,所有 SPDK 内存都将通过 VFIO API 映射到 IOMMU。VFIO 将尝试锁定该内存,并且可能会超过用户对锁定内存的限制。除此之外还会导致各种的 SPDK 错误和故障。
# 解除内存访问限制可使用如下命令配置: --  重启生效
cat >> /etc/security/limits.conf << EOF
root     hard   memlock           unlimited
root     soft   memlock           unlimited
* soft nofile 128000
* hard nofile 128000
EOF

绑定 UIO 或 VFIO 驱动

	为了让 SPDK 控制设备,它必须首先指示操作系统放弃控制。这通常称为取消内核驱动程序与设备的绑定,在 Linux 上是通过写入 sysfs 中的文件来完成的。然后,SPDK 将驱动程序重新绑定到与 Linux 捆绑在一起的两个特殊设备驱动程序 ------ uio 或 vfio。从某种意义上说,这两个驱动程序是"虚拟"驱动程序,因为它们主要向操作系统指示设备绑定了驱动程序,因此它不会自动尝试重新绑定默认驱动程序。他们实际上并没有以任何方式初始化硬件,甚至也不了解它是什么类型的设备。uio 和 vfio 之间的主要区别在于 vfio 能够对平台的 IOMMU 进行编程,IOMMU 是确保用户空间驱动程序内存安全的关键硬件。
    有一个好消息是,对于驱动的解绑和重新绑定操作,SPDK 也为我们提前准备好了脚本,即 SPDK 源码包中的 <scripts/setup.sh> 脚本; 

# scripts/setup.sh 使用指南:
	help    # 显示命令帮助文档
    config  # 默认模式。自动根据系统当前环境执行配置: 1.分配内存大页; 2. 解绑设备传统驱动; 3. 改绑 UIO 或 VFIO 驱动;
	reset   # 将 PCI 设备重新改绑回其原始驱动程序,同时也清理任何剩余的spdk文件/资源,对内存大页不做变动;
    cleanup # 清除SPDK应用程序退出后可能留在系统中的孤立文件
    status  # 打印系统中所有兼容 SPDK 的设备状态 和 内存大页配置状况。

未分配大页时,使用 setup.sh 查询大页配置和设备配置信息截图如下:

在系统未配置 1GB 大页与开启 IOMMU 时, setup.sh 默认配置 2MB 大页和绑定 uio_pci_generic 驱动:

状态查询:

setup.sh 脚本重置对于设备的驱动配置


在系统配置好 1GB 大页与开启 IOMMU 时, setup.sh 默认配置 1GB 大页和绑定 vfio-pci 驱动:

状态查询:

setup.sh 脚本重置对于设备的驱动配置

重置后设备状态查询

开始执行测试

# SPDK 提供了两种形式的 Fio 测试:
	1. 针对 NVME 裸盘的 Fio 测试,其特点为 I/O 通过 SPDK 直接访问裸盘,常被用于评估 SPDK 用户态驱动在裸盘上的性能。
    2. 基于块设备的 Fio 测试,其特点则是 I/O 测试基于块设备之上,所有 I/O 经由块设备层,再传送至裸盘设备,常被用于评估 SPDK 块设备的性能。
    

NVME 裸盘测试

# 基于裸盘 NVME 的 Fio 测试,主要也区分为两种形式:
    1> 本地的NVMe设备,即 NVMe over PCIe;
    2> 远端的NVMe设备,即 NVMe over Fabrics;
    
# 使用时基本和 fio 命令的参数配置相同,只是在具体测试 SPDK 性能的时候,有两点差异。
    第一点: fio 参数配置中的 'ioengine' 参数值要设置为 'spdk';
    第二点: fio 参数配置中的 'filename' 参数值不再以设备盘符名称指定,而是要通过不同形式的裸盘 NVME 的特定格式
来指定,具体格式如下:        
	#1. 本地裸盘 NVME 设备通过 PCI 地址来指定,当有多个盘时,可指定多个该参数,中间以空格分隔即可;
	'--filename=trtype=PCIe traddr=0000:3d:00.0 ns=1'  
    #2. NVMe over  Fabrics(transport=RDMA): 协议 + IP + 端口
    '--filename=trtype=RDMA adrfam=IPv4 traddr=192.168.100.8 trsvcid=4420 ns=1'
    #3. NVMe over Fabrics(transport=TCP):
    '--filename=trtype=TCP adrfam=IPv4 traddr=192.168.100.8 trsvcid=4420 ns=1'
    
# 此外需要注意的是:    	
	现阶段,该裸盘 fio 测试只支持线程模式而不支持多进程模式,因此在 fio 配置参数中,需要特别指定 'thread=1'; 当测试模型为 read/write 时,因为在 random map 时,要额外耗费很多的 CPU cycle,从而会降低性能,故在测试 randread/randwrite 的时候,建议添加指定 fio 参数 'norandommap=1' 。


执行本地 NVME 裸盘 Fio 测试(NVMe over PCIe)
# 命令示例如下(其中的文件路径请根据实际修改):
LD_PRELOAD=/suosuo/spdk-home/spdk/build/fio/spdk_nvme  /suosuo/spdk-home/fio/fio '--filename=trtype=PCIe traddr=0000.03.00.0 ns=1'  --numa_cpu_nodes=0 --output=4K_8_256_randread.log spdk_nvme1.fio
    # 当多盘测试时,可直接追加多个 filename 参数配置 
LD_PRELOAD=/suosuo/spdk-home/spdk/build/fio/spdk_nvme  /suosuo/spdk-home/fio/fio '--filename=trtype=PCIe traddr=0000.38.00.0 ns=1' '--filename=trtype=PCIe traddr=0000.39.00.0 ns=1' '--filename=trtype=PCIe traddr=0000.3a.00.0 ns=1' '--filename=trtype=PCIe traddr=0000.3b.00.0 ns=1' --numa_cpu_nodes=0 --output=4K_8_256_randread.log spdk_nvme1.fio


# Fio  JOB文件 spdk_nvme1.fio  内容如下: --- 根据需要也可自定制 
[global]
ioengine=spdk
thread=1
group_reporting=1
direct=1
verify=0
time_based=1
ramp_time=0

[job]
rw=randread
bs=4k
numjobs=8
iodepth=256
runtime=300

​ 需要注意的是,当 ioengine 配置为 spdk 时,在正式执行测试之前,必须提前通过 SPDK 内置环境变量 LD_PRELOAD 配置可执行文件 spdk_nvme的全路径, 否则 SPDK 的异步轮询 I/O 模型无法真正被 Fio 所应用;除了此种指定 spdk_nvme 文件位置的方式,还有另外一种配置方式,即直接将 spdk_nvme的全路径配置给 Fio Job文件中的 ioengine 参数,而无需再配置 LD_PRELOAD 环境变量;

命令执行截图如下:

执行结果如下图所示: 【盘为大普微的企业级PCIe 4.0 U.2 6.4T NVMe SSD R5300 】

多盘测试时: 【盘为忆联的企业级PCIe 4.0 U.2 6.4T NVMe SSD UH831A 】

执行远程 NVME 裸盘 Fio 测试(NVMe over Fabrics (RDMA/TCP))
执行NVMe over Fabrics(RDMA/TCP)的前提条件是target端要启动nvmf进程
# 启动 nvmf 进程命令实例如下:
./build/bin/nvmf_tgt --json spdk_tgt_nvmf.json
# spdk_tgt_nvmf.json 文件内容如下(需根据实际自定义修改):
{
	"subsystems": [{
		"subsystem": "bdev",
		"config": [{
			"method": "bdev_nvme_attach_controller",
			"params": {
				"name": "Nvme0",
				"trtype": "PCIe",
				"traddr": "0000:81:00.0",
				"prchk_reftag": false,
				"prchk_guard": false
			}
		}]
	}, {
		"subsystem": "nvmf",
		"config": [{
			"method": "nvmf_set_config",
			"params": {
				"acceptor_poll_rate": 10000,
				"admin_cmd_passthru": {
					"identify_ctrlr": false
				}
			}
		}, {
			"method": "nvmf_set_max_subsystems",
			"params": {
				"max_subsystems": 1024
			}
		}, {
			"method": "nvmf_create_transport",
			"params": {
				"trtype": "TCP",
				"max_queue_depth": 128,
				"max_io_qpairs_per_ctrlr": 127,
				"in_capsule_data_size": 4096,
				"max_io_size": 131072,
				"io_unit_size": 24576,
				"max_aq_depth": 128,
				"max_srq_depth": 4096,
				"abort_timeout_sec": 1
			}
		}, {
			"method": "nvmf_create_subsystem",
			"params": {
				"nqn": "nqn.2018-09.io.spdk:cnode1",
				"allow_any_host": true,
				"serial_number": "SPDK001",
				"model_number": "SPDK bdev Controller",
				"max_namespaces": 8
			}
		}, {
			"method": "nvmf_subsystem_add_listener",
			"params": {
				"nqn": "nqn.2018-09.io.spdk:cnode1",
				"listen_address": {
					"trtype": "TCP",
					"adrfam": "IPv4",
					"traddr": "192.168.100.8",
					"trsvcid": "4420"
				}
			}
		}, {
			"method": "nvmf_subsystem_add_ns",
			"params": {
				"nqn": "nqn.2018-09.io.spdk:cnode1",
				"namespace": {
					"nsid": 1,
					"bdev_name": "Nvme0n1",
					"uuid": "51581506-537f-4236-9bc1-d926c966d09b"
				}
			}
		}]
	}]
}

裸盘 NVME --- README.md

Bdev 块设备测试

	基于bdev的fio_plugin是将I/O在SPDK块设备bdev之上进行发送。而基于裸盘的fio_plugin,I/O是直接到裸盘上进行处理。两者最大的差别在于I/O是否经过bdev这一层。因此,基于bdev的fio_plugin能够很好的评估SPDK块设备层bdev的性能。其编译安装与裸盘的fio_plugin完全相同;
执行本地的 NVME bdev Fio 测试
# 本地 bdev Fio 测试命令示例如下:
LD_PRELOAD=/suosuo/spdk-home/spdk/build/fio/spdk_bdev /suosuo/spdk-home/fio/fio --filename='Nvme0n1' --filename='Nvme1n1' --filename='Nvme2n1' --filename='Nvme3n1' --numa_cpu_nodes=0  --output=4K_8_256_randread.log spdk_bdev1.fio
	# 或者将 spdk_bdev 配置和 Fio 参数 filename 配置都写入 Fio JOB 配置文件 
LD_PRELOAD=/suosuo/spdk-home/spdk/build/fio/spdk_bdev /suosuo/spdk-home/fio/fio spdk_bdev1.fio
# spdk_bdev1.fio 文件内容示例如下:
[global]
percentile_list=1:5:10:25:50:75:90:95:99:99.5:99.9:99.99:99.999:99.9999:99.99999:99.999999
ioengine=/suosuo/spdk-home/spdk/build/fio/spdk_bdev
spdk_json_conf=/panda/bdev_spdk/bdev.json
direct=1
thread
group_reporting
time_based
verify=0
ramp_time=30
rw=randread
bs=4k
runtime=200

[filename0]
filename=Nvme0n1
filename=Nvme1n1
filename=Nvme2n1
filename=Nvme3n1
filename=Nvme4n1
filename=Nvme5n1
filename=Nvme6n1
filename=Nvme7n1
filename=Nvme8n1
filename=Nvme9n1
filename=Nvme10n1
filename=Nvme11n1
filename=Nvme12n1
filename=Nvme13n1
filename=Nvme14n1
filename=Nvme15n1
filename=Nvme16n1
filename=Nvme17n1
filename=Nvme18n1
filename=Nvme19n1
filename=Nvme20n1
filename=Nvme21n1
filename=Nvme22n1
filename=Nvme23n1
iodepth=256

# bdev.json 文件的构造, SPDK 提供了生成脚本: ./scripts/gen_nvme.sh; 构造命令如下:
./scripts/gen_nvme.sh --json-with-subsystems > /tmp/bdev.json

命令执行截图如下: 【盘为忆联的企业级PCIe 4.0 U.2 6.4T NVMe SSD UH831A 】

执行远程的 NVME bdev Fio 测试
# bdev.json 文件内容示例如下(RDMA):
{
	"subsystems": [{
		"subsystem": "bdev",
		"config": [{
			"params": {
				"name": "Nvme0",
				"trtype": "rdma",
				"traddr": "192.168.100.8",
				"adrfam": "ipv4",
				"trsvcid": "4420",
				"subnqn": "nqn.2018-09.io.spdk:cnode1"
			},
			"method": "bdev_nvme_attach_controller"
		}]
	}]
}

bdev --- README.md 介绍

SPDK 性能工具 perf 测试

执行本地的 NVME Perf 测试
# 配置大页 和 绑定 SPDK 驱动 
HUGE_EVEN_ALLOC="yes" HUGEMEM=102400 ./scripts/setup.sh

# 执行测试 
./build/examples/perf -c 0xF -q 256 -s 1024 -w randread  -t 100 -r "trtype:PCIe traddr:0000:38:00.0" -o 4096
	# 解释 
    -c  # 掩码绑定 CPU  
    -q  # I/O 队列深度 
    -s  # 内存大页页大小, 单位 MB/s
    -w  # I/O 模式 
    -t  # 测试执行时间
    -r  # 指定测试设备 
    -o  # I/O 块大小 单位 byte 
    
./build/examples/perf -c 0x30 -q 128 -s 1024 -w read -t 60 -r "trtype:PCIe traddr:0000:38:00.0" -o 131072

在系统配置好 1GB 大页与开启 IOMMU 时, 执行测试截图如下:【盘为忆联的企业级PCIe 4.0 U.2 6.4T NVMe SSD UH831A 】


执行远端 NVMeoF 设备的 Perf 测试
# NVMe over PCIe:
perf -q 32 -s 1024 -w randwrite -t 1200 -c 0xF -o 4096 -r 'trtype:PCIe traddr:0000:06:00.0'
# NVMe over Fabrics:
perf -q 32 -s 1024 -w randwrite -t 1200 -c 0xF -o 4096 -r 'trtype:RDMA adrfam:IPv4 traddr:192.0.0.1 trsvcid:4420'

# 对于同时测试多块盘,只需要添加-r并指定设备地址即可,例如一个core测试三块盘:

perf -q 32 -s 1024 -w randwrite -t 1200 -c 0x1 -o 4096 -r 'trtype:PCIe traddr:0000:06:00.0' -r 'trtype:PCIe
traddr:0000:07:00.0' -r 'trtype:PCIe traddr:0000:08:00.0'

执行本地的 NVME 块设备 Perf 测试
# 执行命令示例
./build/examples/perf -q 32 -s 1024 -w randwrite -t 1200 -c 0xF -o 4096 /dev/nvme0n1
  # 多盘执行
./build/examples/perf -q 32 -s 1024 -w randwrite -t 120 -c 0xF0 -o 4096 /dev/nvme0n1 /dev/nvme1n1 /dev/nvme2n1 /dev/nvme3n1

执行测试截图如下: 【盘为忆联的企业级PCIe 4.0 U.2 6.4T NVMe SSD UH831A 】

SPDK 基于 bdev 的 bdevperf 工具测试

  成功编译spdk后,可在spdk/build/examples目录下找到 bdevperf 工具的二进制运行文件。bdevperf 使用方法如下所示:

./build/examples/bdevperf -c <config> -q <I/O depth> -t <time in seconds> -w <io pattern type: write|read|randread|randwrite> -s <huge memory size in MB> -o <I/O
size in bytes> -m <core mask>

# 命令示例如下:
./build/examples/bdevperf -q 32 -s 1024 -w randwrite -t 1200 -o 4096 -m 0xF -c bdevperf.conf

性能监控分析

   为了更好的直观呈现 SPDK 用户态驱动 与 内核态驱动在暴力 I/O 负载下的系统运行指标差异,本人分别对其进行了测试,同时也各自对其进行了 vmstat 系统监控,具体见下图;

   通过日志对比可以直观地看出这 3 个指标数据差异变化大:
    r  # SPDK 下处于运行态的进程更多,进程都在繁忙的处理数据 
    in # 由于 SPDK 技术绕过了内核和采用了异步轮询技术,故而大幅降低了 中断 的次数,将更多的 CPU 时间用于了数据处理,而非中断的响应;
    cs # 同样是 SPDK 绕过了内核,采用了用户态驱动,直接在用户态下操纵硬件设备,避免了数据来来回回地在内核态中内外切换;

自动化配置执行脚本

一、 SPDK + FIO 自动化执行 Shell 脚本

脚本下载 ---- 工具基于 SPDK 环境 进行 NVMe 性能测试

git clone https://gitee.com/suosuo1930/suo2kkfio.git
cd ./suo2kkfio
chmod +x spdk_kkfio.sh
# 在运行脚本前需打开脚本配置头部参数 --- 指定系统下 SPDK 和 FIO 编译安装后源码的放置位置 
# 该脚本默认的 IO 模型是 randread , 第一步配置好后则可直接运行。
# 若要测试其他 IO 模型,暂还需在脚本当中修改其他自定义参数;

该脚本执行截图如下:

二、下载执行测试工具 --- 可选

应用于NVMe SSD测试,单盘测试默认可覆盖多种IO模型,也可根据需求自定义IO模型的测试工具

# 该工具内部具备功能如下:
    1. 硬盘信息收集工具
    2. fio测试工具
    3. fio测试配置文件
    4. spdk工具
关于该工具更多使用方法请参阅该站地址 

参考网址

SPDK --- 官方安装指导手册

SPDK --- 官方用户手册

SPDK运行fio测试

分配大页 -- 官方配置文档

SPDK Applications -- 官方手册

nvme-cli --- Github 维护路径

posted @ 2024-04-29 20:21  梭梭666  阅读(717)  评论(0编辑  收藏  举报
返回顶部