Тестирование пропускной способности NVMe с Python
В настоящее время мне нужно сделать некоторое тестирование пропускной способности. У меня аппаратная настройка: у меня Samsung 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
После монтирования SSD в /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?
1 ответ
Причина, по которой ваша программа на Python работает лучше, чем ваша работа с fio, заключается в том, что это несправедливое сравнение, и они тестируют разные вещи:
Вы запретили fio использовать буферный кеш Linux (используя
buffered=0
что то же самое, что сказатьdirect=1
) сказав это делатьO_DIRECT
операции. С заданием, которое вы указали, fio должен будет отправить одиночную запись 4k, а затем дождаться завершения этой записи на устройстве (и это подтверждение должно пройти полностью назад к fio), прежде чем он сможет отправить следующую.Вашему скрипту Python разрешено отправлять записи, которые могут буферизироваться на нескольких уровнях (например, в пространстве пользователя библиотекой C, а затем снова в буферном кеше ядра), прежде чем касаться вашего SSD. Как правило, это означает, что записи будут накапливаться и объединяться вместе перед отправкой на более низкий уровень, что приводит к более коротким операциям ввода-вывода, которые требуют меньше накладных расходов. Более того, поскольку вы не делаете явной очистки в теории, вам не нужно отправлять ввод-вывод на диск до выхода из вашей программы (на практике это будет зависеть от ряда факторов, таких как количество операций ввода-вывода, которое вы делаете, ОЗУ Linux может выделить для буферов максимальное время, в течение которого файловая система будет хранить грязные данные, как долго вы выполняете ввод-вывод и т. д.)! Ваш
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 немного странная, сильно ограничена (libaio
ioengine является асинхронным, но с глубиной 1 вы не сможете извлечь из этого выгоду, и это еще до того, как мы перейдем к поведению Linux AIO при использовании файловых систем) и по-разному работает с вашей программой на Python. если вы не выполняете значительно больше буферизованных операций ввода-вывода по сравнению с размером самого большого буфера (а в Linux размер буфера ядра масштабируется с использованием оперативной памяти) и если буферизованные операции ввода-вывода малы, упражнение превращается в демонстрацию эффективности буферизации.
Если вам нужна точная производительность устройства 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
СПДК это еще один выбор. Существует пример теста производительности по адресу https://github.com/spdk/spdk/tree/master/examples/nvme/perf.
Pynvme, основанный на SPDK, является расширением Python. Вы можете написать тест производительности с его ioworker().