Чтобы получить чистый Markdown-контент этой страницы, добавьте
.mdк этому URL. Полный индекс документации см. по адресу https://docs.nvidia.com/dynamo/llms.txt. Полное содержимое, включая API reference и примеры SDK, доступно по адресу https://docs.nvidia.com/dynamo/llms-full.txt.
Дизайн KVBM
В этом документе подробно рассматриваются архитектура, компоненты, интеграции с фреймворками через connector API и внутренняя работа Dynamo KV Block Manager (KVBM). Дизайн KVBM вдохновлен KV block managers из SGLang и vLLM и дополнительно опирается на исторические стратегии memory tiering, распространенные в общем GPU-программировании. Подробнее см. Дополнительное чтение.
Компоненты KVBM
Внутренние компоненты Dynamo KVBM
Ядро
- KvBlockManager: Публичная фасадная сущность. Создает и владеет внутренним состоянием, а также предоставляет pools и onboarding API.
- Scheduler: Ограничивает выполнение transfer относительно прогресса модели (завершения итерации/слоя) при интеграции через connector фреймворка (например, vLLM V1).
- Config (config.rs): Описывает размеры модели, page size, выбор layout и runtime-флаги, используемые для построения pools и layouts.
- KvBlockManagerState: Центральный объект, связывающий layouts, storage backends и pools; владеет OffloadManager, метриками и hooks событий.
- Events/Metrics: Компоненты наблюдаемости, которые публикуют counters/gauges и event hooks для интеграции и тестирования.
Layouts и блоки
- LayoutConfig & LayoutType: Переводят tensor shapes в layouts KV cache (layer-separated или fully-contiguous), включая количество blocks и геометрию.
- Blocks & Metadata: Типизированные handles blocks (mutable/immutable), metadata (например, priority) и представления по layer/outer dims; используются для allocation, регистрации и сопоставления по
sequence_hash.
Менеджер передачи
- TransferManager: Асинхронный orchestrator transfer с очередями по путям (Device→Host, Host→Disk, Host→Device, Disk→Device).
Storage и пулы
- Device Pool (G1): KV block pool в GPU-памяти. Выделяет mutable GPU blocks, регистрирует завершенные blocks (immutable), выполняет поиск по
sequence_hashи является целевой областью для onboarding (Host→Device, Disk→Device). - Host Pool (G2): KV block pool в pinned memory CPU. Принимает offload с Device (Device→Host), может выполнять onboarding на Device (Host→Device) и offload на Disk. Использует pinned (page-locked) memory для эффективных CUDA transfer и NIXL I/O.
- Disk Pool (G3): Локальный KV block pool на базе NVMe SSD. Принимает offload с Host (Host→Disk) и предоставляет blocks для onboarding на Device (Disk→Device). NIXL descriptors раскрывают file offsets/regions для zero-copy I/O и опционального GDS.
- Remote Storage (G4): Удаленное или облачное KV block storage. KVBM рассматривает G4 как opaque blob store, доступный через NIXL, не зная о внутренних оптимизациях layout.
Потоки данных KVBM

Потоки данных KVBM от устройства к другим иерархиям памяти
Device → Host (Offload)
- Запускается, когда scheduler connector'а явно запрашивает offload
- Worker выделяет Host block и выполняет копирование CUDA D2H/Custom Kernel
- Host pool регистрирует новый immutable block (дедупликация по
sequence_hash)
Host → Disk (Offload)
- Local Disk (G3): NIXL Write через POSIX; GDS, когда он доступен
- Remote Disk (G4) (сетевые файловые системы вроде NFS/Lustre/GPFS): NIXL Write через POSIX в смонтированную FS; batching и concurrency идентичны
- Запускается по зарегистрированным Host blocks или по явным запросам offload
- Worker выделяет Disk block и выполняет NIXL Write (Host→Disk)
- Disk pool регистрирует новый immutable block (дедупликация по
sequence_hash)
Host → Device (Onboard)
- Вызывается, чтобы перенести host block в GPU memory
- Worker использует предоставленные Device targets и выполняет копирование CUDA H2D/Custom Kernel
- Device pool регистрирует новый immutable block
Disk → Device (Onboard)
- Вызывается, чтобы перенести disk block напрямую в GPU memory
- Worker использует предоставленные Device targets и выполняет NIXL Read (Disk→Device), возможно через GDS
- Device pool регистрирует новый immutable block
Подробности внутренней архитектуры

Внутренняя архитектура и ключевые модули Dynamo KVBM
KvBlockManager как уровень оркестрации
KvBlockManager<H, D> выступает координатором между уровнями памяти - host (CPU), device (GPU) и remote - управляя pool'ами blocks для каждого backend'а и предоставляя единообразные API жизненного цикла blocks. Он отслеживает расположение KV blocks в памяти device (G1), памяти CPU внутри узла и между узлами (G2), локальных/общих SSD (G3) и удаленного хранилища (G4). G1-G4 - это ключевые уровни, которые включает KVBM. Обратите внимание, что KVBM рассматривает storage G4 как opaque blob store, не зная о внутренних оптимизациях layout.
KvBlockManager<H, D> owns:
- A device-side
BlockPool<Device> - A host-side
BlockPool<Host> - A remote NIXL agent that supports communication and memory sharing across nodes
- A block set registry for remote lookup and import/export of block metadata
С точки зрения реализации всю логику содержит KvBlockManagerState: он инициализируется через KvBlockManagerConfig, который объединяет runtime-, model- и layout-конфигурации. NixlOptions добавляет awareness удаленных ресурсов.
Layout blocks и отображение памяти
Каждый block - это 2D-массив [num_layers][page_size × inner_dim]. BlockLayout trait абстрагирует memory layout. Реализация по умолчанию, FullyContiguous, хранит все layers всех blocks в одной области с вычислением stride с учетом alignment:
block_stride_in_bytes = align_up(num_layers × layer_stride, alignment);
CPU- и GPU-pools используют этот memory layout, но применяют storage-specific backends:
DeviceStorage→ CUDA device bufferPinnedStorage→ page-locked host memorySystemStorage→ CPU heap memory (fallback/test)NixlStorage→ remote memory through NIXL RDMA handles (includes storage)
Каждый layout создается с помощью LayoutConfig, а storage либо передается напрямую, либо выделяется через StorageAllocator.
BlockPool и пулы памяти (active и inactive)
Каждый BlockPool<T> (где T - это DeviceStorage, PinnedStorage и т. д.) отслеживает два под-pool'а:
- ActivePool: содержит blocks, которые сейчас используются sequences
- InactivePool: содержит recycled blocks, готовые к allocation (free list)
Когда запрашивается token block (например, get_mutable_block()), allocator извлекает его из InactivePool, переводит состояние и возвращает writable handle. При commit sequence или eviction система сбрасывает blocks и возвращает их в inactive pool.
Машина состояний blocks
Машина состояний (BlockState) отслеживает переходы жизненного цикла block:
| State | Description | Ownership | Valid Actions/Transitions |
|---|---|---|---|
| Reset | Block hasn't been initialized or was reset. No associated sequence. | Held in InactivePool, reusable | init_sequence(salt_hash) → Partial |
| Partial | Block is being filled with tokens for a new sequence. In-progress. | Owned by the sequence creator | add_token() / add_tokens() (accumulate), commit() → Complete, reset() → Reset |
| Complete | Block is fully filled with token data but not yet visible to others. | Still owned by creator thread | register() → Registered, reset() → Reset |
| Registered | Block is finalized and visible for reuse. Available in the deduplication cache. | Shared ownership (global registry) | Auto drop() → triggers Remove event and transitions to Reset |
Допустимые переходы состояний
| From → To | Trigger | Validation |
|---|---|---|
| Reset → Partial | init_sequence(salt_hash) | Must not be in use |
| Partial → Complete | commit() | Must be full |
| Complete → Registered | register() | Must be finalized |
| Registered → Reset | Drop of RegistrationHandle | Automatic |
| Partial → Reset | Aborted sequence | Explicit or drop |
| Complete → Reset | Invalidated | Explicit or drop |
Пример жизненного цикла block
Sequence запрашивает новый KV block:
- Allocator извлекает block из InactivePool → block находится в Reset
init_sequence()→ переход в Partial- Токены добавляются → состояние остается Partial
- При заполнении →
commit()→ состояние становится Complete register()→ block хэшируется и переходит в Registered. Теперь blocks можно использовать для lookup.- При eviction или завершении жизненного цикла →
drop()RAII handle возвращает block в Reset
Управление жизненным циклом с помощью RAII и event plane
Система использует RAII для управления жизненным циклом памяти. Каждый block хранит metadata и состояние регистрации, а регистрация связана с EventManager. При регистрации и drop:
PublishHandleвызывает Register events- Его
dropвызывает Remove events
Этот подход обеспечивает согласованность отслеживания shared memory между workers без необходимости писать явную логику deallocation. События распространяются в Dynamo Events plane. Любой компонент Dynamo, подписанный на events plane, может отслеживать эти изменения. Обратите внимание, что даже storage provider может подписаться на events plane и построить внутреннее представление prefix tree, адаптированное и оптимизированное под конкретную платформу.
Интеграция remote memory через NIXL
NIXL agent предоставляет remote memory buffers через NixlBlockSet, RemoteBlocks и layout descriptors. Основные операции:
nixl_register(): Registers memory region with NIXL runtimeserialize() / deserialize(): Converts layout and memory into transferable descriptorsimport_remote_blockset(): Loads remote node's block layouts into the managerget_remote_blocks_mutable(): Fetches transferable memory views from another node
RemoteBlocks - это легковесная абстракция над shared memory для использования blocks между узлами (через UCX или другие backends).
Протокол регистрации remote memory
Ниже описан двунаправленный протокол регистрации remote memory и синхронизации layout между workers (например, Worker 1 и Worker 2) с использованием NIXL:
1. Создание agent и регистрация памяти
Каждый worker независимо создает NixlAgent:
- регистрирует свои regions памяти (то есть device memory) через
nixl_register() - эти regions соответствуют blocks, которыми управляет локальный BlockPool
После регистрации памяти NIXL создает descriptors, доступные удаленно, и привязывает их к memory layout.
2. Обмен metadata
После регистрации памяти workers обмениваются сериализованными metadata layout, упакованными в SerializedNixlBlockLayout.
Почему этот шаг критически важен?
- inference workloads для LLM часто различаются по конфигурации tensor parallel (TP):
- у Worker 1 может быть TP=4, а у Worker 2 - TP=8
- даже если обе системы используют похожие layouts
FullyContiguous, их внутренние допущения по slicing и alignment различаются
- обмен metadata устраняет это семантическое несоответствие, передавая:
LayoutConfig(num_layers,page_size,inner_dim,dtype)BlockSetID- базовый адрес + информацию о stride (включая alignment)
Device ID+ тип памяти (host/device)
- после обмена metadata каждый worker может восстановить layout на своей стороне с помощью
deserialize()
Это позволяет NIXL:
- понимать, где находится каждый layer/block
- выполнять корректные операции gather-scatter во время RDMA-подобных transfer
Без этого шага remote fetch привел бы к повреждению данных или неправильному выравниванию токенов.
3. Сериализация и десериализация: переносимость layouts
На этапе сериализации KVBM экспортирует, а FullyContiguous::serialize() кодирует:
- FullyContiguousConfig
- base_offset
- Physical memory descriptors (NixlStorage), including:
- Memory type (VRAM, DRAM)
- Address & size
- Device ID
Система отправляет это через NIXL transfer, а затем внедряет в состояние scheduler KVBM.
На этапе десериализации SerializedNixlBlockLayout::deserialize() восстанавливает это в:
- A fully reconstructed memory layout view
- Local representation of a remote memory slice with correct offsets and size semantics
Это также обеспечивает прямой доступ к remote memory с едиными логическими семантиками. Тем самым гарантируется, что даже при разных конфигурациях системы (аппаратных или по форме LLM) обе стороны одинаково понимают memory view каждого KV block.
4. Ownership handles и отслеживание lifetime
Ownership памяти в NIXL тесно связана с RAII-based handles:
- когда block регистрируется, он возвращает
PublishHandle, который оборачиваетRegistrationHandle - при
dropэтого handle автоматически публикуется Remove event, который:- deregister'ит block из слоя NIXL
- удаляет его из реестра remote blocks
- это гарантирует, что после eviction block из cache или прекращения использования в inference все ссылки на него корректно инвалидируются между узлами
Этот механизм предотвращает:
- обращение к устаревшей памяти
- dangling pointers на GPU или host
- ошибки ручной deregistration
Система может пакетировать и публиковать registration events с помощью Publisher, оптимизируя производительность при высокой concurrency.
Storage backends и расширяемость
Можно интегрировать KVBM с storage backend'ом, расширив или обернув NixlEnabledStorage для поддержки cross-node RDMA registration. Все layouts и block pools являются generic по отношению к этим backend'ам, что дает тонкий контроль над memory tiers.
---
title: Example KVBM System Architecture
---
flowchart TD
A["Distributed Inference Engine"] --> B["Dynamo KV Block Manager"]
B --> C["NIXL Storage Agent<br/>- Volume registration<br/>- get()/put() abstraction"]
B --> D["Event Plane<br/>- Pub/Sub (NATS or ZMQ)<br/>- StoreEvent / RemoveEvent"]
C --> E["G4 Storage Infrastructure<br/>(SSD, Object store, etc.)<br/>- Store KV blocks"]
D --> F["Storage Provider Subscriber<br/>- Parse Events<br/>- Build fast tree/index<br/>- Optimize G4 tiering"]
NIXL Storage Interface (для интеграции backend'ов)
NIXL interface абстрагирует работу с volume и отделяет ее от mounting, отслеживания metadata или прямого system I/O. Он предоставляет:
registerVolume(descriptor): Register a logical volume for KV cache dataunregisterVolume(): Cleanly deregister and release volume mappingsget() / put(): Block-level APIs used by KVBM to fetch and store token blocks
Эти абстракции позволяют интегрировать backend'ы без привязки к файловому стеку host'а, обеспечивая безопасное взаимодействие с block devices, локальными файловыми системами и RDMA-capable volumes. Обратите внимание, что эти API все еще дорабатываются.
Dynamo Event Plane (слой координации Pub/Sub)
Чтобы поддержать оптимизации внешнего storage без изменения логики KVBM, мы предоставляем event plane (поддерживает транспорты NATS и ZMQ), который публикует lifecycle events для всех операций с blocks:
- StoreEvent: публикуется, когда KV block регистрируется
- RemoveEvent: публикуется, когда KV block освобождается или вытесняется
Каждый KVEvent (~100 bytes) содержит:
| Field | Description |
|---|---|
sequence_hash | Уникальный идентификатор KV block |
prefix_hash | Группировка префиксов для агрегации на уровне запросов |
block_size | Размер в байтах |
storage_location | Идентификатор логического volume |
event_type | Store или Remove |
extra_metadata | Зарезервированные поля для оптимизаций партнера |
Для масштабируемости система пакетирует и публикует эти события периодически (например, каждые ~10s или динамически, исходя из нагрузки).
Концептуальный дизайн Storage Advisor
Этот раздел дает обзор для storage provider'ов, которые хотят интегрироваться как custom backend для KVBM. Это необязательный путь интеграции KVBM с backend'ом.
Внешние storage systems не жестко связаны с execution pipeline Dynamo. Вместо этого они пассивно наблюдают lifecycle events KV blocks через модель подписки:
- Storage volumes заранее подготовлены и смонтированы storage provider'ом
- Эти volumes регистрируются в Dynamo через NIXL Storage Agent с помощью API
registerVolume() - Dynamo KV Block Manager взаимодействует только с логическими API на уровне blocks (
get()иput()) - Event Plane асинхронно транслирует lifecycle events KV через pub/sub (NATS или ZMQ)
- Вендоры storage реализуют легковесный subscriber process, который слушает эти события
Чтобы обеспечить быстрый lookup и dynamic tiering, storage vendor'ы могут строить внутренние структуры данных на основе полученного event stream:
- При получении StoreEvent: добавить запись во внутреннее prefix tree, hash map или LRU index с
prefix_hash,sequence_hashи связанной metadata - При получении RemoveEvent: удалить или обрезать соответствующую запись, при необходимости запустив cleanup или tier migration workflows
Имея в реальном времени видимость паттернов использования KV blocks, storage system может реализовывать интеллектуальные политики tiering:
- Hot block promotion: часто используемые KV blocks можно переносить на быстрые SSD volumes
- Cold block demotion: редко используемые blocks можно переносить в более медленное storage (HDD, cloud object storage)
- Proactive compaction: если размер blocks или паттерны префиксов указывают на фрагментацию, storage backend может объединять или переписывать blocks
Этот дизайн обеспечивает, чтобы производительность, отказоустойчивость и расширяемость масштабировались независимо на уровне KV и на уровне storage backend.
Интеграции с фреймворками
KVBM интегрируется с inference frameworks (SGLang, TensorRT-LLM, vLLM) через Connector API, чтобы влиять на поведение KV caching, планирование и выполнение forward pass.
Архитектура Connector
Интерфейс состоит из двух компонентов:
- Scheduler (Leader): отвечает за orchestration offload/onboard KV blocks, строит metadata, определяющие transfer data для workers. Также содержит hooks для обработки завершения асинхронного transfer.
- Worker: отвечает за чтение metadata, построенных scheduler'ом (leader), и выполняет асинхронный onboarding/offloading в конце forward pass.

Типичная интеграция KVBM с inference frameworks (в качестве примера показан vLLM)
Операции онбординга

Onboarding blocks from Host to Device

Onboarding blocks from Disk to Device
Операции оффлоадинга

Offloading blocks from Device to Host & Disk
Дополнительное чтение
- vLLM Automatic Prefix Caching
- SGLang HiCache Benchmarks
- EMOGI: Efficient Memory-access for Out-of-memory Graph-traversal