В эпоху больших языковых моделей (LLM) и сложных нейросетей данные стали таким же критическим ресурсом, как и вычислительная мощность. Согласно отчету Stanford AI Index за 2025 год, вычислительная мощность для значимых AI-моделей удваивается примерно каждые пять месяцев, в то время как размер наборов данных для обучения увеличивается каждые восемь месяцев. Этот дисбаланс создает узкое горлышко: если данные не поступают на ускорители достаточно быстро, мощные GPU простаивают, ожидая загрузки информации. Традиционный путь передачи данных — через память CPU — становится неприемлемым bottleneck. Здесь на сцену выходит технология NVIDIA Magnum IO GPUDirect Storage (GDS), которая позволяет передавать данные напрямую между хранилищем и памятью GPU, минуя процессор. Однако для разработчиков на Python, которые составляют основу современного AI-стека, прямой доступ к низкоуровневым API часто сложен. Именно здесь вступает в игру библиотека NVIDIA KvikIO, предоставляющая высокоуровневый интерфейс для работы с GDS.
В этой статье мы подробно разберем, как использовать KvikIO для эффективного чтения данных в память GPU, как настроить параметры для оптимизации производительности и как диагностировать, какой именно путь передачи данных был использован системой. Мы рассмотрим архитектуру взаимодействия слоев, практические примеры кода, тонкости настройки параметров cuFile и KvikIO, а также реальные кейсы использования этой технологии в индустрии. Это руководство предназначено для инженеров по машинному обучению и разработчиков, стремящихся выжать максимум из своей инфраструктуры.
01Архитектура взаимодействия: KvikIO, cuFile и GPUDirect Storage
Чтобы понять, как работает KvikIO, необходимо рассмотреть иерархию компонентов, участвующих в процессе ввода-вывода. GPUDirect Storage (GDS) — это более широкая технология, обеспечивающая ускоренное перемещение данных между поддерживаемым хранилищем и памятью GPU. Для хранения на основе файловых систем (например, NVMe SSD) основным интерфейсом является библиотека cuFile (C API). Она предоставляет функции для регистрации буферов GPU и выполнения операций I/O.
KvikIO располагается над cuFile, выступая в роли высокоуровневого адаптера. Он предоставляет удобные интерфейсы на Python и C++, которые позволяют разработчикам работать с файлами так же естественно, как с обычными объектами, но с возможностью управления деталями передачи данных. Важно понимать, что выбор пути передачи данных происходит на двух независимых уровнях:
- Уровень KvikIO: Библиотека решает, использовать ли cuFile для выполнения запроса или применить собственный POSIX-совместимый путь, который предполагает буферизацию данных через память CPU.
- Уровень cuFile: Если KvikIO передал запрос в cuFile, то сама библиотека cuFile решает, использовать ли ускоренный путь через модуль ядра
nvidia-fs(GDS) или fallback-путь через CPU.
Это разделение ответственности дает гибкость, но требует понимания того, как каждый уровень влияет на итоговую производительность. Вызов API cuFile не гарантирует, что данные прошли через GDS; это лишь означает, что KvikIO выбрал этот путь для обработки запроса.

02Требования к системе и подготовка окружения
Перед тем как приступить к работе, необходимо убедиться, что ваша инфраструктура готова к использованию GPUDirect Storage. Для успешного выполнения задач требуется следующая конфигурация:
- Операционная система: Linux-система с установленным драйвером NVIDIA GPU и пакетом GPUDirect Storage.
- Хранилище: Устройство хранения, поддерживаемое GDS (например, NVMe SSD), и файловая система, совместимая с GDS (например, ext4, XFS, Btrfs).
- Топология PCIe: Желательно наличие PIX-соединения (PCIe eXpress Direct) между выбранным GPU и устройством NVMe, что означает прохождение пути через не более одного моста PCIe. Это минимизирует задержки.
- Настройки BIOS/Kernel: Для обеспечения прямых передач между устройствами PCIe (peer-to-peer) необходимо отключить IOMMU и ACS (Access Control Services) в настройках системы.
- Модуль ядра: Должен быть загружен модуль
nvidia-fs, который обеспечивает ускоренный путь передачи данных.
Вы можете проверить топологию GPU и хранилища с помощью команды:
$ nvidia-smi topo -nvmeДля установки KvikIO рекомендуется создать изолированное виртуальное окружение Python. Ниже приведен пример установки библиотеки для CUDA 13:
$ python3 -m venv kvikio-env
$ source kvikio-env/bin/activate
$ python -m pip install kvikio-cu13После установки убедитесь, что зависимости (включая CuPy) были корректно установлены:
$ python -m pip show kvikio-cu13 cupy-cuda13x
03Шаг 1: Инспекция конфигурации и параметров
Прежде чем выполнять операции ввода-вывода, критически важно проверить текущие настройки KvikIO и cuFile. По умолчанию KvikIO работает в режиме AUTO, что означает попытку использовать cuFile с автоматическим падением в режим POSIX, если cuFile недоступен. Также существует параметр gds_threshold, определяющий минимальный размер запроса, при котором KvikIO обращается к cuFile. Запросы меньшего размера обрабатываются напрямую через POSIX-путь KvikIO.
Вы можете проверить текущие настройки, используя следующий Python-код:
import kvikio
import kvikio.cufile_driver as cufile_driver
import kvikio.defaults as defaults
print(f"KvikIO version: {kvikio.__version__}")
print(f"cuFile version: {cufile_driver.libcufile_version()}")
print(f"Compatibility mode: {defaults.get('compat_mode').name}")
print(f"GDS threshold: {defaults.get('gds_threshold')}")На типичной тестовой системе вывод будет выглядеть так: режим совместимости AUTO и порог GDS 16 KiB. Эти параметры можно изменять как через переменные окружения (KVIKIO_COMPAT_MODE, KVIKIO_GDS_THRESHOLD), так и программно:
# Установка нескольких параметров сразу
defaults.set({"task_size": 1024**3, "num_threads": 8})
# Отключение режима совместимости (принудительное использование cuFile)
defaults.set("compat_mode", kvikio.CompatMode.OFF)Кроме того, полезно запустить диагностический скрипт GDS, чтобы убедиться, что система распознает все компоненты:
$ /usr/local/cuda-13.3/gds/tools/gdscheck.py -p 2>&1 | less -R
task_size в KvikIO определяет размер чанков, на которые разбивается большой запрос на чтение. Оптимальное значение зависит от размера ваших данных и возможностей GPU. Слишком маленькие чанки увеличивают накладные расходы на планирование, слишком большие могут привести к неэффективному использованию памяти.04Шаг 2: Чтение файла в память GPU
Теперь, когда окружение настроено, перейдем к практической части. Создадим тестовый файл размером 1 Гигабайт с случайными данными. Использование прямого ввода-вывода (direct I/O) и синхронизации с диском (fsync) гарантирует, что данные физически записаны на хранилище:
$ dd if=/dev/urandom \
of=/mnt/nvme1/kvikio-tutorial/kvikio-example.bin \
bs=1M count=1024 \
oflag=direct conv=fsync \
status=progressСледующий код демонстрирует, как прочитать этот файл непосредственно в буфер GPU, используя библиотеку CuPy и KvikIO:
from pathlib import Path
import cupy as cp
import kvikio
file_path = Path("/mnt/nvme1/kvikio-tutorial/kvikio-example.bin")
# Выделение буфера в памяти GPU
gpu_buffer = cp.empty(
file_path.stat().st_size,
dtype=cp.uint8
)
# Чтение файла в буфер GPU
with kvikio.CuFile(file_path, "r") as file:
bytes_read = file.read(gpu_buffer)
# Проверка результата
assert bytes_read == gpu_buffer.nbytes
print(f"Successfully read {bytes_read} bytes into GPU memory.")Контекстный менеджер with обеспечивает корректное открытие файла и выполнение операции чтения. Возвращаемое значение подтверждает объем прочитанных байт, но, как мы увидим далее, оно не говорит нам о том, какой физический путь был использован для передачи данных.

05Шаг 3: Диагностика пути передачи данных
Самый важный аспект работы с GDS — это понимание того, прошли ли данные через ускоренный путь nvidia-fs или через эмуляцию через CPU. Для этого в cuFile встроена система статистики, которую можно включить с помощью переменной окружения CUFILE_STATS_LEVEL=3.
Чтобы проанализировать статистику, необходимо запустить скрипт в интерактивном режиме Python, чтобы процесс оставался активным после выполнения операции чтения. Затем мы можем использовать утилиту gds_stats для просмотра накопленных счетчиков:
$ CUFILE_STATS_LEVEL=3 python3 -i read_with_kvikio.pyПосле выполнения кода в интерактивной оболочке введите:
>>> import kvikio.cufile_driver as cufile_driver
cufile_driver.gds_stats()В выводе статистики вы увидите два ключевых счетчика:
nvfs: Количество операций, прошедших через модуль ядраnvidia-fs(ускоренный путь GDS).posix: Количество операций, обработанных через совместимый путь cuFile (через CPU).
В типичном успешном сценарии для файла 1 ГБ вы увидите, что все 256 операций чтения (разбитых по умолчанию на чанки по 4 МБ) прошли через nvfs, а posix равен нулю. Это подтверждает, что данные были загружены напрямую в GPU, минуя CPU.

06Шаг 4: Сравнение путей и настройка параметров
Давайте проверим, как изменение параметров влияет на выбор пути. Мы можем принудительно заставить KvikIO использовать cuFile, отключив режим совместимости:
$ CUFILE_STATS_LEVEL=3 \
KVIKIO_COMPAT_MODE=off \
python3 -i read_with_kvikio.pyВ этом случае gds_stats все равно покажет операции nvfs, так как cuFile на своей стороне также выберет ускоренный путь, если система позволяет. Однако, если мы принудительно заставим cuFile использовать совместимый режим, ситуация изменится:
$ CUFILE_STATS_LEVEL=3 \
KVIKIO_COMPAT_MODE=off \
CUFILE_FORCE_COMPAT_MODE=true \
python3 -i read_with_kvikio.pyТеперь операции будут зафиксированы в счетчике posix, так как cuFile намеренно избегает использования nvidia-fs. Наконец, если мы включим режим совместимости на уровне KvikIO:
$ KVIKIO_COMPAT_MODE=on \
python3 read_with_kvikio.pyKvikIO вообще не будет вызывать cuFile, а выполнит чтение через свой собственный POSIX-бэкенд с буферизацией в CPU. В этом случае cuFile не зафиксирует операцию, так как он вообще не участвовал в процессе.
07Шаг 5: Внутренняя конфигурация cuFile
После того как KvikIO выбрал путь через cuFile, сама библиотека cuFile применяет дополнительные настройки, определяющие, как запрос будет разбит на подзадачи и распараллелен. Эти настройки задаются в файле cufile.json и работают независимо от параметров KvikIO.
Ключевые параметры cuFile включают:
execution.parallel_io: Включает внутреннее параллельное обработка в cuFile.execution.max_io_threads: Ограничивает количество рабочих потоков cuFile для каждой очереди GPU.execution.min_io_threshold_size_kb: Устанавливает целевой минимальный размер для внутренних запланированных кусков данных.execution.max_request_parallelism: Ограничивает степень параллелизма, которую cuFile готовит для одного запроса.
Рассмотрим пример: если KvikIO отправляет один запрос на 1 ГБ, а в cufile.json установлено max_io_threads=4 и max_request_parallelism=4, то cuFile разобьет этот запрос на 4 части по 256 МБ. Однако, если буфер GPU не зарегистрирован для прямого DMA, cuFile будет использовать "bounce buffer" (буфер-посредник) в памяти GPU, копируя данные из него в целевой буфер. В этом случае, даже если логический размер куска 256 МБ, физическая передача через nvidia-fs может быть разбита на более мелкие чанки (например, по 16 МБ), что зависит от внутренней реализации slabs в GDS.
min_io_threshold_size_kb не всегда определяет размер физического сетевого или дискового пакета. Он влияет на планирование задач внутри cuFile. Фактический размер передачи может быть меньше из-за ограничений драйвера или модуля nvidia-fs.08Практическое применение в экосистеме NVIDIA
KvikIO является частью библиотеки NVIDIA CUDA-X для науки о данных (ранее известной как RAPIDS). Его интеграция с GDS изначально была разработана для библиотеки cuDF (GPU DataFrame), где параллельное чтение файлов Parquet и ORC через GDS значительно ускорило загрузку данных. Эта архитектура была перенесена в KvikIO, что сделало его универсальным инструментом.
Сегодня KvikIO используется в различных критически важных приложениях:
- cuCIM: Для цифровых патологических исследований, где требуется обработка огромных изображений высокого разрешения.
- cuPhoton: Для анализа научных изображений, где скорость загрузки данных напрямую влияет на время вывода моделей.
- GPU-accelerated Presto: Для аналитических рабочих нагрузок, демонстрирующих преимущества GDS в запросах к большим данным.
Эти примеры показывают, что KvikIO — это не просто утилита для чтения файлов, а фундаментальный слой ввода-вывода, который позволяет приложениям AI и аналитики полностью использовать потенциал современных GPU-хранилищ.
09Что это значит на практике
Для разработчиков AI и ML внедрение KvikIO и GPUDirect Storage означает переход от модели, где GPU ждет данные, к модели, где данные поступают на GPU с пропускной способностью, близкой к физической границе шины PCIe. Это особенно важно для:
- Обучения больших моделей: Ускорение загрузки датасетов сокращает время цикла обучения, позволяя исследователям быстрее итерировать эксперименты.
- Инференса в реальном времени: В сценариях, где требуется быстрая подгрузка весов моделей или входных данных, задержки, вызванные копированием через CPU, могут стать критичными.
- Эффективности использования ресурсов: Освобождение CPU от задач копирования данных позволяет ему заниматься другими задачами, такими как предобработка данных или управление оркестраторами.
Для достижения максимальной производительности рекомендуется:
- Всегда проверять статистику
gds_statsпосле внедрения изменений. - Настраивать
task_sizeв KvikIO и параметры параллелизма вcufile.jsonпод конкретный объем данных и топологию системы. - Использовать зарегистрированные буферы GPU, если это возможно, чтобы избежать накладных расходов на копирование через bounce buffers.
Изучение документации NVIDIA GPUDirect Storage и эксперименты с настройками KvikIO на вашей конкретной инфраструктуре — лучший способ понять, как извлечь максимум из этой мощной технологии.
Источник: NVIDIA Developer ↗
