使用 Python 进行 NVMe 吞吐量测试



目前我需要做一些吞吐量测试。我的硬件设置是我有一个三星 950 Pro 连接到一个 NVMe 控制器,该控制器通过和 PCIe 端口连接到主板。我有一个 Linux nvme 设备,该设备与我挂载在文件系统上的某个位置的设备相对应。

我希望使用Python来做到这一点。我计划在挂载 SSD 的文件系统上打开一个文件,记录时间,将一些 n 长度的字节流写入文件,记录时间,然后使用 os 模块文件操作实用程序关闭文件。这是测量写入吞吐量的函数。

def perform_timed_write(num_bytes, blocksize, fd):
"""
This function writes to file and records the time
The function has three steps. The first is to write, the second is to
record time, and the third is to calculate the rate.
Parameters
----------
num_bytes: int
blocksize that needs to be written to the file
fd: string
location on filesystem to write to
Returns
-------
bytes_per_second: float
rate of transfer
"""
# generate random string
random_byte_string = os.urandom(blocksize)
# open the file
write_file = os.open(fd, os.O_CREAT | os.O_WRONLY | os.O_NONBLOCK)        
# set time, write, record time
bytes_written = 0
before_write = time.clock()
while bytes_written < num_bytes:
os.write(write_file, random_byte_string)
bytes_written += blocksize
after_write = time.clock()
#close the file
os.close(write_file)
# calculate elapsed time
elapsed_time = after_write - before_write
# calculate bytes per second
bytes_per_second = num_bytes / elapsed_time

return bytes_per_second

我的另一种测试方法是使用Linux fio实用程序。 https://linux.die.net/man/1/fio

在/fsmnt/fs1 上安装固态硬盘后,我使用此作业文件来测试吞吐量

;Write to 1 file on partition
[global]
ioengine=libaio
buffered=0
rw=write
bs=4k
size=1g
openfiles=1
[file1]
directory=/fsmnt/fs1

我注意到从 Python 函数返回的写入速度明显高于 fio。因为Python是如此高级,所以你放弃了很多控制权。我想知道Python是否在幕后做一些事情来欺骗它的速度。有谁知道为什么Python生成的写入速度会比fio生成的速度高得多?

你的Python程序比你的fio工作做得更好的原因是因为这不是一个公平的比较,他们正在测试不同的东西:

  • 你禁止fio使用Linux的缓冲区缓存(通过使用buffered=0,这与说direct=1相同),告诉它做O_DIRECT操作。使用您指定的作业,fio 必须发送单个 4k 写入,然后等待该写入在设备上完成(并且该确认必须一直返回到 fio),然后才能发送下一个。

  • 在接触 SSD 之前,您的 Python 脚本可以发送可以在多个级别缓冲的写入(例如,在用户空间内由 C 库,然后在内核的缓冲区缓存中再次缓冲)。这通常意味着写入将在发送到较低级别之前累积并合并在一起,从而导致开销更小的 I/O 大块。此外,由于理论上您不执行任何显式刷新,因此在程序退出之前不必将I/O发送到磁盘(实际上,这将取决于许多因素,例如执行多少I/O,Linux可以为缓冲区留出的RAM量,文件系统保存脏数据的最长时间, 你做多长时间的 I/O 等)!您的os.close(write_file)将变成一个fclose(),在其 Linux 手册页中这样说:

    请注意,fclose() 仅刷新 C 库提供的用户空间缓冲区。为了确保数据物理存储在磁盘上,还必须刷新内核缓冲区,例如,使用 sync(2) 或 fsync(2)。

    事实上,您在调用os.close()之前花费了最后的时间,因此您甚至可能省略了最终"批次"数据仅发送到内核所花费的时间,更不用说SSD!

你的 Python 脚本更接近这个 fio 作业:

[global]
ioengine=psync
rw=write
bs=4k
size=1g
[file1]
filename=/fsmnt/fio.tmp

即使有这个fio仍然处于劣势,因为你的Python程序有用户空间缓冲(所以bs=8k可能更接近)。

关键要点是你的 Python 程序并没有真正在你指定的块大小下测试你的 SSD 的速度,你原来的 fio 作业有点奇怪,受到严格限制(libaioioengine 是异步的,但深度为 1,你将无法从中受益,这是在我们了解 Linux AIO 在使用文件系统时的行为之前),并对你的 Python 程序做不同的事情。 如果与最大缓冲区的大小相比,您没有执行更多的缓冲 I/O(在 Linux 上,内核的缓冲区大小随 RAM 而缩放),如果缓冲的 I/O 很小,练习将变成缓冲有效性的演示。

如果您需要NVMe设备的确切性能,fio是最佳选择。FIO可以直接将测试数据写入设备,无需任何文件系统。下面是一个示例:

[global]
ioengine=libaio
invalidate=1
iodepth=32
time_based
direct=1
filename=/dev/nvme0n1
[write-nvme]
stonewall
bs=128K
rw=write
numjobs=1
runtime=10000

SPDK是另一种选择。在 https://github.com/spdk/spdk/tree/master/examples/nvme/perf 有一个性能测试的现有示例。

Pynvme基于SPDK,是一个Python扩展。您可以使用其 ioworker() 编写性能测试。

最新更新