Тестирование пропускной способности 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().

Другие вопросы по тегам