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

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

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

См. также