Files
2026-07-13 12:24:33 +08:00

161 lines
6.9 KiB
Plaintext

// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "mem_kernels.cuh" // TransferDirection, EngineKVFormat
#include <c10/cuda/CUDAGuard.h>
#include <cstdint>
#include <vector>
struct PageBufferShapeDesc {
int kv_size; // 1 or 2
int nl; // num layers
int nb; // num blocks
int bs; // block size
int nh; // num heads
int hs; // head size
int element_size; // bytes (1 or 2)
// Physical per-block stride in source-dtype element units, used by
// formats whose dim-0 is the block axis to step over padding bytes
// (e.g. DeepSeek V4 compressor / indexer caches sharing a vLLM KV
// pool with larger attn groups, whose rows are padded up to the
// pool's max row width). 0 means "unset — fall back to the
// format-specific tight stride".
//
// CONTRACT: pass ``tensor.stride(0)`` verbatim. PyTorch stride
// semantics already absorb every inner-dim extent (including
// ``kv_size``), so DO NOT pre-multiply by any inner dim.
//
// Honoured today only by NL_X_NB_BS_HS (per-layer [NB, BS, HS],
// MLA). NL_X_NB_TWO_BS_NH_HS is restricted to the tight form
// upstream and leaves this field at 0; all other formats either
// pack non-block info into dim-0 or do not support dim-0 padding,
// and ignore this field.
int block_stride_elems;
template <typename ScalarType>
__host__ __device__ inline size_t scalars_per_head() const {
return hs * element_size / sizeof(ScalarType);
}
template <typename ScalarType>
__host__ __device__ inline size_t scalars_per_token() const {
return nh * hs * element_size / sizeof(ScalarType);
}
// Per (K or V) block step along dim-0, expressed in ``ScalarType``
// element units (the kernel's working dtype, e.g. uint4 / uint32_t /
// uint16_t). Returns the tight ``bs * nh * hs`` by default, or the
// physical ``block_stride_elems`` when dim-0 carries padding (today
// only NL_X_NB_BS_HS, see ``block_stride_elems`` above). Every
// ``calculate_engine_global_offset`` branch uses this as the dim-0
// step, so honouring padding here propagates to all formats without
// per-branch changes.
template <typename ScalarType>
__host__ __device__ inline size_t scalars_per_block() const {
const size_t elems = block_stride_elems > 0
? static_cast<size_t>(block_stride_elems)
: static_cast<size_t>(bs) * nh * hs;
return elems * element_size / sizeof(ScalarType);
}
};
template <typename ScalarType>
struct MemoryObj4 {
ScalarType* objects[4];
int num_objects; // 0 - 4
};
// ---------------------------------------------------------------------------
// Object-group transfer plan.
//
// A whole object group's transfer (all staging copies + all kernel launches) is
// described as a plan on the Python side, then executed in a single native call
// (execute_object_group_transfer) that releases the GIL once for the entire
// burst instead of once per copy/launch. See the design in
// docs/design/v1/multiprocess/modules/ and lmcache_driven_transfer.py.
// ---------------------------------------------------------------------------
// One asynchronous host<->device copy. `host_offset` is the host-side virtual
// offset in the lmcache allocator (source for H2D, destination for D2H).
struct StagingCopy {
uintptr_t dest;
uintptr_t src;
size_t nbytes;
size_t host_offset;
};
// One kernel launch within a batch step. The batch-invariant arguments live in
// the referenced KernelGroupSpec; only these vary per (batch, kernel group).
struct LaunchVar {
int group_idx; // index into the plan's kernel_group_specs
int64_t block_ids_offset; // element offset into the group's block_ids_base
int total_blocks; // number of block ids for this launch
int num_objects; // chunks in this batch (1-4)
int skip_prefix_n_blocks;
};
// One batch: its staging copies and kernel launches. For H2D the staging runs
// before the launches, for D2H after; the executor preserves this ordering.
struct BatchStep {
std::vector<StagingCopy> staging;
std::vector<LaunchVar> launches;
};
// Per-kernel-group invariants, resolved once on the Python side.
struct KernelGroupSpec {
uintptr_t paged_buffer_ptrs; // device ptr-array base address
std::vector<int64_t> lmcache_objects_ptrs; // temp GPU buffer ptr per slot
PageBufferShapeDesc shape_desc;
int lmcache_chunk_size;
EngineKVFormat engine_kv_format;
uintptr_t block_ids_base; // device int64* base; sliced via block_ids_offset
int64_t block_ids_capacity; // total int64 elements behind block_ids_base;
// bounds-checks each slice in the executor
};
/**
* Execute one object group's transfer plan on the current CUDA stream.
*
* Enqueues every staging copy and kernel launch described by `batch_steps`
* within a single GIL release (configured at the pybind layer), eliminating the
* per-copy/per-launch GIL handoffs of the equivalent Python loop. The device
* guard and stream are set once for the whole plan.
*
* @param direction H2D (retrieve) or D2H (store), applied to all ops
* @param device CUDA device of the transfer
* @param host_buffer_alignment Host buffer alignment for staging copies
* (power of two)
* @param kernel_group_specs Per-kernel-group invariants
* @param batch_steps Ordered per-batch staging + launch work
*/
void execute_object_group_transfer(
TransferDirection direction, const torch::Device& device,
size_t host_buffer_alignment,
const std::vector<KernelGroupSpec>& kernel_group_specs,
const std::vector<BatchStep>& batch_steps);
/**
* Block-level multi-layer KV transfer between vLLM paged buffers and
* LMCache contiguous memory objects.
*
* @param paged_buffer_ptrs_tensor GPU int64 tensor of data pointers into
* vLLM paged buffers (one per tensor)
* @param lmcache_objects_ptrs Raw pointers to LMCache memory objects
* @param block_ids GPU int64 tensor of block indices in vLLM
* paged buffer
* @param device CUDA device of vLLM tensors
* @param direction H2D (LMCache->vLLM) or D2H (vLLM->LMCache)
* @param shape_desc Shape descriptor for the paged buffer
* @param lmcache_chunk_size Tokens per LMCache memory object
* @param engine_kv_format EngineKVFormat identifier
* @param skip_prefix_n_blocks Number of blocks to skip at the beginning
*/
void multi_layer_block_kv_transfer(
const torch::Tensor& paged_buffer_ptrs_tensor,
std::vector<int64_t> lmcache_objects_ptrs, const torch::Tensor& block_ids,
const torch::Device& device, TransferDirection direction,
PageBufferShapeDesc shape_desc, int lmcache_chunk_size,
EngineKVFormat engine_kv_format, int skip_prefix_n_blocks);