Перейти к основному содержимому

Чтобы получить чистый 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​

Internal Components of Dynamo 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 Data Flows

Потоки данных 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

Подробности внутренней архитектуры​

Internal architecture and key modules in the Dynamo KVBM

Внутренняя архитектура и ключевые модули 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 buffer
  • PinnedStorage → page-locked host memory
  • SystemStorage → 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:

StateDescriptionOwnershipValid Actions/Transitions
ResetBlock hasn't been initialized or was reset. No associated sequence.Held in InactivePool, reusableinit_sequence(salt_hash) → Partial
PartialBlock is being filled with tokens for a new sequence. In-progress.Owned by the sequence creatoradd_token() / add_tokens() (accumulate), commit() → Complete, reset() → Reset
CompleteBlock is fully filled with token data but not yet visible to others.Still owned by creator threadregister() → Registered, reset() → Reset
RegisteredBlock 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 → ToTriggerValidation
Reset → Partialinit_sequence(salt_hash)Must not be in use
Partial → Completecommit()Must be full
Complete → Registeredregister()Must be finalized
Registered → ResetDrop of RegistrationHandleAutomatic
Partial → ResetAborted sequenceExplicit or drop
Complete → ResetInvalidatedExplicit or drop

Пример жизненного цикла block​

Sequence запрашивает новый KV block:

  1. Allocator извлекает block из InactivePool → block находится в Reset
  2. init_sequence() → переход в Partial
  3. Токены добавляются → состояние остается Partial
  4. При заполнении → commit() → состояние становится Complete
  5. register() → block хэшируется и переходит в Registered. Теперь blocks можно использовать для lookup.
  6. При 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 runtime
  • serialize() / deserialize(): Converts layout and memory into transferable descriptors
  • import_remote_blockset(): Loads remote node's block layouts into the manager
  • get_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 data
  • unregisterVolume(): Cleanly deregister and release volume mappings
  • get() / 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) содержит:

FieldDescription
sequence_hashУникальный идентификатор KV block
prefix_hashГруппировка префиксов для агрегации на уровне запросов
block_sizeРазмер в байтах
storage_locationИдентификатор логического volume
event_typeStore или Remove
extra_metadataЗарезервированные поля для оптимизаций партнера

Для масштабируемости система пакетирует и публикует эти события периодически (например, каждые ~10s или динамически, исходя из нагрузки).

Концептуальный дизайн Storage Advisor​

Этот раздел дает обзор для storage provider'ов, которые хотят интегрироваться как custom backend для KVBM. Это необязательный путь интеграции KVBM с backend'ом.

Внешние storage systems не жестко связаны с execution pipeline Dynamo. Вместо этого они пассивно наблюдают lifecycle events KV blocks через модель подписки:

  1. Storage volumes заранее подготовлены и смонтированы storage provider'ом
  2. Эти volumes регистрируются в Dynamo через NIXL Storage Agent с помощью API registerVolume()
  3. Dynamo KV Block Manager взаимодействует только с логическими API на уровне blocks (get() и put())
  4. Event Plane асинхронно транслирует lifecycle events KV через pub/sub (NATS или ZMQ)
  5. Вендоры 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.

vLLM KVBM Integration

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

Операции онбординга​

Onboarding blocks from Host to Device

Onboarding blocks from Host to Device

Onboarding blocks from Disk to Device

Onboarding blocks from Disk to Device

Операции оффлоадинга​

Offloading blocks from Device to Host & Disk

Offloading blocks from Device to Host & Disk

Дополнительное чтение​

См. также​