存儲性能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 libaio libaio-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
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" } } }] }] }
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"
}]
}]
}
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 模型,暫還需在腳本當中修改其他自定義參數;
該腳本執行截圖如下:

二、下載執行測試工具 --- 可選
# 該工具內部具備功能如下:
1. 硬盤信息收集工具
2. fio測試工具
3. fio測試配置文件
4. spdk工具
關于該工具更多使用方法請參閱該站地址

浙公網安備 33010602011771號