English translation is not available yet. Showing Russian content.
Написать RDMA-читалку для KV cache
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Написать RDMA-читалку для KV cache
1. Цель задачи
Разработать низкоуровневый компонент (RDMA-читалку) для передачи KV cache между узлами распределённой LLM-системы через InfiniBand с использованием Remote Direct Memory Access (RDMA). Цель — обеспечить латентность передачи 1 GB данных менее 50 микросекунд, что критично для синхронизации кэша при инференсе больших моделей. Вы научитесь работать с libibverbs, регистрацией памяти и односторонними RDMA-операциями (RDMA Read).
Ключевой результат Рабочая программа на C, которая по RDMA Read получает KV cache заданного размера с удалённого узла и замеряет latency передачи.
2. Исходные данные
Перед началом необходимо иметь:
| Что нужно | Откуда взять |
|---|---|
| Два Linux-сервера с InfiniBand (или один с SoftRoCE) | Физический кластер / облачный инстанс с IB / локальная виртуализация |
| RDMA-совместимое сетевое оборудование (HCA) | Mellanox ConnectX-5 или выше / эмуляция SoftRoCE |
| Установленный RDMA-стек (rdma-core, libibverbs, librdmacm) | Пакетный менеджер (apt, yum) или сборка из исходников |
| Компилятор C (gcc/clang) и make | Система сборки |
| Тестовый KV cache (бинарный файл размером 1 GB) | Сгенерировать случайными данными (dd if=/dev/urandom of=kv_cache.bin bs=1M count=1024) |
| Измеритель времени (clock_gettime с CLOCK_MONOTONIC) | Стандартная библиотека POSIX |
Если нет реального InfiniBand — симулируем:
- Установите SoftRoCE (RXE) на одном Linux-хосте:
sudo apt install rdma-core librdmacm-dev libibverbs-dev sudo modprobe rdma_rxe sudo rxe_add enp0s3 # замените на ваш интерфейс - Проверьте, что появилось устройство
rxe0(команда ibv_devinfo). - Используйте loopback (127.0.0.1) для тестов — RDMA будет работать через SoftRoCE, но latency будет выше (цель <50 мкс может не достигаться, но код будет рабочим).
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | C (C11) | Низкоуровневый доступ к RDMA, минимальные накладные расходы |
| RDMA API | libibverbs (ibv_*) | Регистрация памяти, создание QP, RDMA Read |
| Управление соединениями | librdmacm (rdma_*) | Установка RC-соединения между узлами |
| Сборка | Makefile / CMake | Автоматизация компиляции |
| Профилирование | perf, ibv_asyncwatch | Измерение latency, мониторинг ошибок |
| Генерация данных | dd, /dev/urandom | Создание тестового KV cache |
| Тестирование | bash-скрипты, pingpong | Проверка корректности и производительности |
4. Этапы выполнения
Этап 1: Настройка окружения и проверка RDMA-стека (1 час)
Действия
- Установите RDMA-пакеты на обоих узлах (или на одном для SoftRoCE):
sudo apt update && sudo apt install -y rdma-core librdmacm-dev libibverbs-dev ibverbs-utils perftest - Проверьте устройства InfiniBand:
Убедитесь, что видно хотя бы одно устройство (например,ibv_devinfomlx5_0илиrxe0). - Проверьте соединение между узлами через RDMA (если два физических узла):
Убедитесь, что bandwidth тест проходит.# На сервере: ib_send_bw -d mlx5_0 -p 12345 # На клиенте: ib_send_bw -d mlx5_0 -p 12345 <server_ip> - Создайте тестовый KV cache (1 GB):
dd if=/dev/urandom of=kv_cache.bin bs=1M count=1024 - Напишите Makefile для сборки проекта:
CC = gcc CFLAGS = -Wall -O2 -march=native LDFLAGS = -libverbs -lrdmacm -lpthread all: rdma_kv_reader rdma_kv_server rdma_kv_reader: rdma_kv_reader.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) rdma_kv_server: rdma_kv_server.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) clean: rm -f rdma_kv_reader rdma_kv_server
Ожидаемый результат этапа Рабочее RDMA-соединение между узлами (или loopback), скомпилированный каркас программы.
Этап 2: Разработка RDMA-сервера (регистрация памяти и ожидание запросов) (2 часа)
Действия
- Создайте файл
rdma_kv_server.c. - Инициализируйте контекст RDMA:
- Получите device list через
ibv_get_device_list. - Откройте устройство (
ibv_open_device). - Создайте Protection Domain (
ibv_alloc_pd).
- Получите device list через
- Зарегистрируйте память для KV cache:
Гдеstruct ibv_mr *mr = ibv_reg_mr(pd, kv_cache, size, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ);kv_cache— буфер, загруженный из файла. - Создайте Completion Channel и CQ:
struct ibv_comp_channel *channel = ibv_create_comp_channel(ctx); struct ibv_cq *cq = ibv_create_cq(ctx, 10, NULL, channel, 0); - Создайте Queue Pair (QP) типа RC:
struct ibv_qp_init_attr attr = { .send_cq = cq, .recv_cq = cq, .qp_type = IBV_QPT_RC, .cap = { .max_send_wr = 1, .max_recv_wr = 1, .max_send_sge = 1, .max_recv_sge = 1 } }; struct ibv_qp *qp = ibv_create_qp(pd, &attr); - Переведите QP в состояние RTR и RTS (используйте
rdma_connectили ручной обмен через сокеты). Для простоты используйте librdmacm:- Создайте
rdma_cm_idи слушайте на порту. - При подключении получите remote key и адрес.
- Создайте
- Передайте клиенту rkey и адрес буфера (через TCP или встроенный протокол).
- Ожидайте завершения RDMA-операций через ibv_poll_cq.
Ожидаемый результат этапа Сервер, который регистрирует 1 GB буфер и ожидает RDMA Read запросов от клиента.
Этап 3: Разработка RDMA-клиента (читалка) (2 часа)
Действия
- Создайте файл
rdma_kv_reader.c. - Инициализируйте RDMA-контекст (аналогично серверу, но без регистрации большого буфера — достаточно маленького для приёма).
- Подключитесь к серверу через
rdma_connect. - Получите remote key (rkey) и remote address от сервера (через TCP-канал или как часть handshake).
- Зарегистрируйте локальный буфер для приёма данных (также 1 GB):
struct ibv_mr *local_mr = ibv_reg_mr(pd, local_buf, size, IBV_ACCESS_LOCAL_WRITE); - Постройте scatter-gather element (SGE):
struct ibv_sge sge = { .addr = (uintptr_t)local_buf, .length = size, .lkey = local_mr->lkey }; - Постройте RDMA Read Work Request (WR):
struct ibv_send_wr wr = { .wr_id = 1, .sg_list = &sge, .num_sge = 1, .opcode = IBV_WR_RDMA_READ, .send_flags = IBV_SEND_SIGNALED, .wr.rdma = { .remote_addr = remote_addr, .rkey = remote_rkey } }; - Отправьте WR на QP (ibv_post_send).
- Дождитесь completion (poll CQ) и замерьте время:
struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); // post_send // poll CQ clock_gettime(CLOCK_MONOTONIC, &end); double latency = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec); // ns - Проверьте корректность — сравните полученные данные с оригинальным файлом (например, md5sum).
Ожидаемый результат этапа Клиент, который выполняет RDMA Read 1 GB с сервера и выводит latency.
Этап 4: Оптимизация и измерение latency (1 час)
Действия
- Запустите тест на физическом InfiniBand (или SoftRoCE) и запишите latency.
- Оптимизируйте параметры:
- Увеличьте размер MTU (если возможно) до 4 KB.
- Используйте
IBV_SEND_INLINEдля маленьких сообщений (не применимо для 1 GB). - Настройте количество WQE (max_send_wr) и размер CQ.
- Попробуйте
ibv_fork_init()для оптимизации fork-безопасности.
- Повторите замеры 10 раз, вычислите среднее, медиану, p99.
- Сравните с целевым значением (<50 мкс). Если не достигается, проанализируйте узкие места:
- Документируйте результаты в отчёте.
Ожидаемый результат этапа Измеренная latency и рекомендации по дальнейшей оптимизации.
Этап 5: Интеграция с KV cache (формат данных) и тестирование (1 час)
Действия
- Определите формат KV cache (например, последовательность пар ключ-значение с заголовками). Для простоты используйте плоский массив байт.
- Модифицируйте сервер для загрузки реального KV cache из файла (если не сделано ранее).
- Напишите скрипт для автоматического запуска:
# server.sh ./rdma_kv_server -p 12345 -f kv_cache.bin # reader.sh ./rdma_kv_reader -s <server_ip> -p 12345 -o received.bin - Проверьте целостность:
md5sum kv_cache.bin received.bin - Проведите нагрузочное тестирование — 100 последовательных запросов, замерьте latency каждого.
Ожидаемый результат этапа Рабочая связка сервер-клиент с проверкой корректности и статистикой latency.
5. Критерии приемки (Definition of Done)
- Код компилируется без ошибок и предупреждений (флаги
-Wall -Wextra). - Сервер и клиент обмениваются данными через RDMA Read.
- Latency передачи 1 GB данных < 50 мкс на реальном InfiniBand (на SoftRoCE допускается > 50 мкс, но код должен быть рабочим).
- Данные, полученные клиентом, идентичны оригинальному файлу (проверка md5).
- Программа корректно обрабатывает ошибки (неверный адрес, потеря соединения).
- Время выполнения одной передачи не превышает 100 мс (с учётом накладных расходов).
- Присутствует Makefile и README с инструкцией по запуску.
- Код использует только libibverbs и librdmacm (без сторонних RDMA-библиотек).
- Реализован замер времени с точностью до наносекунд.
- Поддерживается передача произвольного размера (не только 1 GB) через аргументы командной строки.
6. Ожидаемый результат
Основной артефакт Исходный код на C (два файла: rdma_kv_server.c, rdma_kv_reader.c) и Makefile.
Содержание
- Реализация RDMA-сервера: регистрация памяти, ожидание подключения, обработка RDMA Read.
- Реализация RDMA-клиента: подключение, получение remote key, выполнение RDMA Read, замер latency.
- Скрипты для запуска (опционально).
Дополнительные результаты
- Отчёт с результатами измерений latency (среднее, медиана, p99) для разных размеров (1 MB, 10 MB, 100 MB, 1 GB).
- График зависимости latency от размера (если есть возможность построить).
- Анализ узких места (например, влияние регистрации памяти, размера MTU).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Нет физического InfiniBand | Использовать SoftRoCE (RXE) на одном хосте; latency будет выше, но код будет рабочим. |
Ошибка ibv_reg_mr для больших буферов (не хватает памяти) | Увеличить max_locked_memory через ulimit -l unlimited или настроить /etc/security/limits.conf. |
| RDMA Read не завершается (CQ пуст) | Проверить, что QP переведён в RTS; использовать ibv_poll_cq с таймаутом; включить асинхронные события. |
| Несоответствие remote key | Убедиться, что rkey передан корректно (endianness, целостность). |
| Высокая latency (>50 мкс) | Проверить использование IBV_SEND_SIGNALED; уменьшить количество WQE; использовать ibv_fork_init(). |
| Segmentation fault при работе с буферами | Проверить выравнивание адресов (страницы); использовать posix_memalign. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Этап 1: Настройка окружения | 1 час |
| Этап 2: Разработка сервера | 2 часа |
| Этап 3: Разработка клиента | 2 часа |
| Этап 4: Оптимизация и замеры | 1 час |
| Этап 5: Интеграция и тестирование | 1 час |
| Итого | 7 часов |
Примечание Для первого раза с незнакомым RDMA API рекомендуется заложить дополнительно 2-3 часа на отладку.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 12 | Основы InfiniBand и RDMA |
| 45 | Регистрация памяти в libibverbs |
| 78 | Создание Queue Pair и переходы состояний |
| 112 | RDMA Read vs RDMA Write |
| 156 | Zero-copy передача данных |
| 203 | Оптимизация latency в RDMA |
| 267 | Использование librdmacm для установки соединения |
| 334 | Обработка completion events |
| 401 | Сравнение InfiniBand и Ethernet (RoCE) |
| 512 | KV cache в распределённом инференсе LLM |
10. Чек-лист самопроверки
- Я проверил, что код компилируется с
-Wall -Wextraбез ошибок. - Я протестировал передачу 1 GB данных и убедился, что md5 совпадает.
- Я измерил latency минимум 10 раз и записал среднее значение.
- Я убедился, что программа корректно обрабатывает ошибки (неверный порт, отсутствие устройства).
- Я написал README с инструкцией по сборке и запуску, включая настройку SoftRoCE.
- Я проверил, что latency на физическом InfiniBand (если доступен) < 50 мкс.
- Я освобождаю все ресурсы (pd, mr, qp, cq, device) при завершении.