Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3bc36a5
[None][feat] add unified KV cache layout foundation
allisonlim-nv Oct 12, 2026
dad9f1f
[None][refactor] simplify unified KV cache layer specs
allisonlim-nv Oct 12, 2026
6ea5ad8
[None][test] remove unified KV cache foundation tests
allisonlim-nv Oct 12, 2026
090cbc0
[None][docs] remove unified KV cache design document
allisonlim-nv Oct 12, 2026
511975f
[None][feat] support per-layer storage layouts in KVCacheManagerV2
allisonlim-nv Oct 13, 2026
bfc8bc7
fix: normalize explicit KV cache spec windows
allisonlim-nv Oct 13, 2026
18c71c1
fix: use explicit KV cache dtype for cold-page codec
allisonlim-nv Oct 13, 2026
ae64dca
test: cover explicit KV cache dtype for cold-page codec
allisonlim-nv Oct 13, 2026
701abea
fix: honor physical slot strides in KV cache views
allisonlim-nv Oct 13, 2026
623816c
test: cover mixed KV cache odd-slot addressing
allisonlim-nv Oct 14, 2026
7cde28e
fix: restore KV cache subclass and test compatibility
allisonlim-nv Oct 14, 2026
4a3bb25
[None][test] fix mixed KV cache slot address arithmetic
allisonlim-nv Oct 14, 2026
9d0107b
[None][fix] align NVFP4 data and scale page layouts
allisonlim-nv Oct 15, 2026
e273a98
[None][fix] accept mixed KV cache pointer table layouts
allisonlim-nv Oct 16, 2026
f6b1d08
[None][fix] preserve sink and reuse policy in KV cache costing
allisonlim-nv Oct 16, 2026
eea6dcb
Merge main into unified KV cache manager foundation
allisonlim-nv Oct 16, 2026
636715a
fix: initialize layer specs in FP4 MLA sizing fixture
allisonlim-nv Oct 16, 2026
9d9a487
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 16, 2026
77d4eae
[None][fix] Release DSA test cache managers with metadata
allisonlim-nv Oct 18, 2026
ad5ca5a
Merge main into unified KV cache manager foundation
allisonlim-nv Oct 19, 2026
61248ec
[None][fix] Reject odd NVFP4 layer-spec head dimensions
allisonlim-nv Oct 19, 2026
705f6bb
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 19, 2026
797d01d
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 19, 2026
346c9f1
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 20, 2026
0818f57
Merge main into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 20, 2026
ebc5325
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 20, 2026
7eae54c
Merge main into unified KV cache foundation branch
allisonlim-nv Oct 22, 2026
c67ba3f
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 22, 2026
c49cf44
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 23, 2026
d1ab571
fix: reject draft-owned layer specs with Helix context parallelism
allisonlim-nv Oct 23, 2026
6206118
fix: reject layer specs for cache managers with extra buffers
allisonlim-nv Oct 23, 2026
5f5b93b
Merge upstream/main into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 23, 2026
b069cdf
fix: initialize layer specs in KDA draft record test fixture
allisonlim-nv Oct 23, 2026
eadaf05
fix: count explicit KV cache capacity from allocator slots
allisonlim-nv Oct 23, 2026
ded3a12
fix: use explicit KV geometry for accepted-token relocation
allisonlim-nv Oct 23, 2026
6b2580a
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 23, 2026
cffb88f
Merge main into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 24, 2026
458b907
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 24, 2026
4cc4eb5
Merge branch 'main' into feat/unified-kv-cache-manager-foundation
allisonlim-nv Oct 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ struct BufferConfig
std::optional<int> tokensPerBlockOverride;

bool isSparse = false; //!< Whether history uses the sparse attention lifecycle.

//! Buffers may coalesce only when their group and expanded size match.
//! This does not change their lifecycle or shared slot allocation.
int coalescingGroup = 0;
};

// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,13 @@ StorageConfig createStorageConfig(KVCacheManagerConfig const& config)
LifeCycleRegistry registry{config};
int tokensPerBlock = config.tokensPerBlock;

// Map: lifeCycleId → (bufferSize → list of BufferId).
// Outer map key is LifeCycleId; inner map key is expanded buffer size.
// Map: lifeCycleId → ((coalescingGroup, expanded buffer size) → list of BufferId).
// NOTE: Python uses insertion-ordered dict/defaultdict here. The std::map
// grouping below intentionally remains sorted, which can make observable
// pool-group indices/layout order differ from Python. Later lookups use
// StorageConfig mappings, so this is not expected to affect correctness.
std::map<LifeCycleId, std::map<size_t, std::vector<BufferId>>> bufferGroups;
using CoalescingKey = std::pair<int, size_t>;
std::map<LifeCycleId, std::map<CoalescingKey, std::vector<BufferId>>> bufferGroups;
std::unordered_map<BufferId, int, BufferIdHash> expansionMap;

for (auto const& layer : config.layers)
Expand All @@ -179,7 +179,7 @@ StorageConfig createStorageConfig(KVCacheManagerConfig const& config)

BufferId bid{cfg.layerId, buf.role};
expansionMap[bid] = exp;
bufferGroups[lcId][expandedSize].push_back(bid);
bufferGroups[lcId][{buf.coalescingGroup, expandedSize}].push_back(bid);
}
},
layer);
Expand All @@ -189,14 +189,14 @@ StorageConfig createStorageConfig(KVCacheManagerConfig const& config)
// Each variant: coalesced buffers sorted by size descending.
std::vector<SlotDescVariant> slotGroups;
slotGroups.reserve(bufferGroups.size());
for (auto const& [lcId, sizeMap] : bufferGroups)
for (auto const& [lcId, coalescingMap] : bufferGroups)
{
SlotDescVariant var;
var.lifeCycleId = lcId;
for (auto const& [sz, bufIds] : sizeMap)
for (auto const& [key, bufIds] : coalescingMap)
{
CoalescedBuffer cb;
cb.singleBufferSize = sz;
cb.singleBufferSize = key.second;
cb.bufferIds = bufIds;
var.coalescedBuffers.push_back(std::move(cb));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1616,12 +1616,14 @@ void KvCacheManagerV2Bindings::initBindings(nb::module_& m)
.def("validate", &kv::DiskCacheTierConfig::validate) DEF_COPY(kv::DiskCacheTierConfig);

nb::class_<kv::BufferConfig>(m, "BufferConfig")
.def(nb::init<kv::DataRole, size_t, std::optional<int>, bool>(), nb::arg("role"), nb::arg("size"),
nb::arg("tokens_per_block_override") = std::nullopt, nb::arg("is_sparse") = false)
.def(nb::init<kv::DataRole, size_t, std::optional<int>, bool, int>(), nb::arg("role"), nb::arg("size"),
nb::arg("tokens_per_block_override") = std::nullopt, nb::arg("is_sparse") = false,
nb::arg("coalescing_group") = 0)
.def_rw("role", &kv::BufferConfig::role)
.def_rw("size", &kv::BufferConfig::size)
.def_rw("tokens_per_block_override", &kv::BufferConfig::tokensPerBlockOverride)
.def_rw("is_sparse", &kv::BufferConfig::isSparse) DEF_COPY(kv::BufferConfig);
.def_rw("is_sparse", &kv::BufferConfig::isSparse)
.def_rw("coalescing_group", &kv::BufferConfig::coalescingGroup) DEF_COPY(kv::BufferConfig);

nb::class_<kv::AttentionLayerConfig>(m, "AttentionLayerConfig")
.def(nb::init<kv::LayerId, std::vector<kv::BufferConfig>, std::optional<int>, std::optional<int>,
Expand Down
9 changes: 6 additions & 3 deletions cpp/tensorrt_llm/thop/attentionOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1717,14 +1717,17 @@ KvCachePoolPointers buildKvCachePoolPointers(at::Tensor const& hostKvCachePoolPo
}
else
{
TORCH_CHECK(hostKvCachePoolPointers.dim() == 2);
TORCH_CHECK(hostKvCachePoolPointers.dim() == 2 || hostKvCachePoolPointers.dim() == 3);
// Non-FP4 layers use only the data pointers in a mixed-format pool table.
auto const dataPointers
= hostKvCachePoolPointers.dim() == 3 ? hostKvCachePoolPointers.select(2, 0) : hostKvCachePoolPointers;
pointers.primaryPoolPtr = reinterpret_cast<void*>(
reinterpret_cast<char*>(
readHostTensor2D<int64_t>(hostKvCachePoolPointers, poolIndex, 0, "host_kv_cache_pool_pointers"))
readHostTensor2D<int64_t>(dataPointers, poolIndex, 0, "host_kv_cache_pool_pointers"))
+ intraPoolOffset);
pointers.secondaryPoolPtr = reinterpret_cast<void*>(
reinterpret_cast<char*>(
readHostTensor2D<int64_t>(hostKvCachePoolPointers, poolIndex, 1, "host_kv_cache_pool_pointers"))
readHostTensor2D<int64_t>(dataPointers, poolIndex, 1, "host_kv_cache_pool_pointers"))
+ intraPoolOffset);
}
return pointers;
Expand Down
11 changes: 7 additions & 4 deletions tensorrt_llm/_torch/attention/backends/flashinfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1211,20 +1211,23 @@ def _post_init_with_buffers(self, buffers) -> None:
self._max_num_blocks_per_seq = self.kv_cache_manager.max_blocks_per_seq

# Layers may share one page-index list only when they are in the
# same pool AND have the same page-index scale: VSWA splits pools,
# same pool, page-index scale, and KV factor: VSWA splits pools,
# and per-layer geometry (e.g. Gemma4 sliding/global head_dim)
# splits scales even when all windows collapse to max_seq_len and
# is_vswa is False. Guarded on V2-specific attributes (V1 managers
# lack the per-pool infrastructure).
# is_vswa is False. Mixed K-only/KV layers can use different view
# indices even with the same physical scale. Guarded on V2-specific
# attributes (V1 managers lack the per-pool infrastructure).
mgr = self.kv_cache_manager
get_scale = getattr(mgr, 'get_layer_page_index_scale', None)
get_kv_factor = getattr(mgr, 'get_layer_kv_factor', None)
layer_space: Dict[int, int] = {}
if hasattr(mgr, 'layer_to_pool_mapping_dict') and get_scale:
space_ids = {}
for layer_idx in attention_layer_indices:
layer_offset = mgr.layer_offsets[layer_idx]
key = (mgr.layer_to_pool_mapping_dict[layer_offset],
get_scale(layer_idx))
get_scale(layer_idx),
get_kv_factor(layer_idx) if get_kv_factor else None)
if (self._has_mixed_precision_kv()
and layer_offset in mgr.high_layers):
key = ("mixed_precision_staging", )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ def _get_batch_cache_indices_by_pool_id(
is_kv_aggregate: bool = True,
num_blocks_per_seq: Optional[Sequence[int]] = None,
index_scale: Optional[int] = None,
kv_factor: Optional[int] = None,
):
"""Return page indices; padded entries remain ``BAD_PAGE_INDEX`` (-1).

Expand Down Expand Up @@ -1015,6 +1016,8 @@ def _get_batch_cache_indices_by_pool_id(
index_scale: Kept for compatibility with the base virtual method;
M3 bypasses the V1 block-id conversion entirely, so any
caller-supplied scale is ignored alongside ``index_scales``.
kv_factor: Kept for compatibility with the base virtual method;
M3 returns raw slot IDs without KV aggregation.
"""
res = []
for req_idx, req_id in enumerate(request_ids):
Expand Down
Loading
Loading