KsanaLLM
repository·main·Indexed 20 days ago
https://github.com/tencent/ksanallmA high-performance LLM inference and serving engine optimized for NVIDIA GPUs and Huawei Ascend NPUs. It features advanced memory management, kernel optimizations for high throughput, and tools for tuning Mixture-of-Experts (MoE) kernels via fused_moe.py. The project includes support for Marlin MoE WNA16 operators, DeepSeek-V4 Flash FP4 inference, GEMM algorithm optimization, and a Mooncake Mock Store for functional verification.
What's inside KsanaLLM
- The Benchmarking Tool is designed to measure the performance of various LLM serving backends, including KsanaLLM, vLLM, TensorRT-LLM, and SGLang. It provides metrics such as throughput, latency, and Time To First Token (TTFT). Additionally, it features automatic diff checking to ensure model output consistency across runs.
Overview of KsanaLLM
mainKsanaLLM is a high-performance and easy-to-use inference engine designed for Large Language Model (LLM) inference and serving. It is optimized for high throughput and supports both NVIDIA GPUs and Huawei Ascend NPUs.
Key Performance Features
- Optimized Kernels: Utilizes high-performance operators from projects like vLLM, TensorRT-LLM, FasterTransformer, SGLang, and LightLLM.
- Memory Management: Implements PagedAttention for efficient KV cache management.
- Dynamic Batching: Fine-tuned task scheduling and memory footprint optimization.
- Advanced Features: Supports Prefix caching and DeepSeek-MTP.
Key Usability Features
- Model Compatibility: Seamlessly integrates with Hugging Face models (supports PyTorch and SafeTensor formats).
- Serving Capabilities: Supports high-throughput serving with multiple decoding algorithms (e.g., parallel sampling, beam search), streaming output, and an OpenAI-compatible API server.
- Parallelism: Supports tensor parallelism across multiple cards.
Supported Hardware
- NVIDIA GPUs: A10, A100, L40, L20, H20
- Huawei Ascend NPUs: 910B2C
Marlin MoE WNA16 Operator Migration Overview
mainThis document details the migration of the Marlin MoE WNA16 operator from the SGLang project to the
ksanallmrepository. The migration focuses on interface adaptation rather than changing the core kernel algorithm logic.Key Source Information:
- Source Project: SGLang
- Source Repository:
https://github.com/sgl-project/sglang.git - Reference Commit:
b69485ff4272b5a306045fa0cf7fc2c5692d391f - External Namespace: The migrated operator is exposed under the
llm_kernels::nvidia::marlin_moe_wna16namespace.
What is a weight_map.json file?
mainThe
weight_map.jsonfile is a mapping table used to bridge the gap between custom model weight names and the standard Llama model weight names. It is specifically used for models based on the Llama architecture that have modified parameter names.In the mapping:
- The key acts as a regular expression string (
std::basic_regex) used for matching. - The value acts as a format string (
fmt) used for the replacement.
This allows the engine to automatically rename custom weights to the expected Llama format during loading.
- The key acts as a regular expression string (
What is EPLB (Expert-Parallel Load Balancer)?
mainEPLB is a mechanism designed to optimize Expert-Parallel (EP) inference in Mixture-of-Experts (MoE) models.
In standard EP, experts are distributed uniformly across GPUs (e.g., in an EP8 setup with 256 experts, each of the 8 GPUs gets 32 experts). However, because different experts are activated with different frequencies ("hot experts"), the amount of data sent to each GPU via
AllToAllcommunication becomes imbalanced. This imbalance causes some GPUs to be overloaded while others remain idle, leading to synchronization delays during the subsequentCombinestep and reducing overall system throughput.EPLB addresses this by remapping experts to GPUs based on their activation frequency, ensuring a more balanced workload across the cluster.
What is ModelPerf Backend and how does it work?
mainModelPerfBackendis a pure performance testing mode designed for the event-driven scheduler. Unlike the standard inference mode, it does not accept external RPC/HTTP requests. Instead, it follows a deterministic execution flow:- It reads a
batch_config.jsonfile to construct fixed batches of requests. - It executes multiple rounds of
warmup(to stabilize performance) andstat(to collect metrics). - It collects scheduling and operator-level latency data.
- It writes the results to a JSON file (specified by
output_fileinmodel_perf_config.json) and then automatically exits the process.
This mode is intended for performance profiling and simulator fitting rather than serving live traffic.
InferenceEngine::Start() └─ ModelPerfBackend::StartPerf() # Blocks until all batches complete for each batch in batches: 1. Construct InferRequest, submit via AddInferRequest 2. ModelPerfBackend::Schedule() # Multi-round (warmup + stat) scheduling 3. UpdateWithGenerationResult() # Collect metrics per round 4. batch_waiter returns, move to next batch Write model_perf_result.json └─ InferenceEngine::Stop() → Process exits- It reads a
EPLB Roadmap and Optimization Stages
mainThe EPLB project follows a progressive optimization strategy:
- Stage 1: Basic EP Inference: Foundation for expert-parallel inference.
- Stage 2: Static Expert Assignment (Current): Uses offline analysis of expert activation to determine fixed expert placement on GPUs.
- Stage 3: Static Redundant Experts: Deploys 4-8 expert replicas per GPU to mitigate hot-expert bottlenecks.
- Stage 4: Dynamic Request Routing (LPLB): Introduces request-level load balancing based on real-time replica load.
- Stage 5: Dynamic Redundant Experts: Periodically updates expert replicas based on changing access patterns.
- Stage 6: Full Dynamic EPLB: Complete dynamic management including global reordering and on-demand loading/unloading.
Handling Stop and Recompute events via ApplyBackendEventsToState
mainThe
ApplyBackendEventsToState()method uses a "two-stage processing" approach forStoporRecomputeevents to ensure request integrity:- Stage 1: Requests are immediately removed from the
ScheduleStateandDpBatch.running_reqs. - Stage 2: If
HasInflightTask()returnstrue, the requests are written to a delayed queue. The actual request-level operations are only executed once the conditions forProcessDelayedBackendEvents()are met.
- Stage 1: Requests are immediately removed from the
Understand the Remote Cache Loading workflow
mainRemote Cache Loading is an optimized process for loading prefix cache data from remote storage. The workflow follows a specific state machine and sequence of API calls to manage FA (Flash Attention) and SWA (Streaming Window Attention) blocks during loading.
Typical Workflow Sequence
- Check Cache Status: Call
GetRequestPrefixBlockNumberBeforeRemoteCacheLoadingto determine the current cache hit range. - Allocate Resources: Call
AllocateBlocksForRemoteCacheto reserve FA and SWA blocks for the incoming remote data. - Perform Remote Load: The caller (Engine) performs the actual data transfer from remote storage using the allocated block IDs.
- Confirm Results: Call
ConfirmRemoteCacheLoadingResultto finalize the process. This step transfers successfully matched SWA blocks to their actual FA block positions and releases any unneeded blocks. - Resume Inference: Continue inference using the updated
cached_blocks.
Request States during Loading
While a request is in the
remote_loadingstate, certain inference-related interfaces are prohibited:AllocateRequestBlocksUpdateRequestTokensAppendFilledCachedBlock
Note that the state is tracked via
is_remote_loadingandis_remote_loading_block_allocatedwithin thePrefixCachedRequeststructure./* Sequence of operations for an Engine */ 1. GetRequestPrefixBlockNumberBeforeRemoteCacheLoading() 2. AllocateBlocksForRemoteCache(fa_block_num, swa_block_num) 3. [Perform Remote Loading] 4. ConfirmRemoteCacheLoadingResult(matched_block_index, matched_remote_swa_block_indexs, reserved_block_num) 5. [Continue Inference]- Check Cache Status: Call
How SWA window validity is determined during prefix cache matching
mainWhen matching prefixes in the cache, a matched endpoint is only considered valid if it satisfies the Continuous Window Condition. Because SWA requires every token to see its previous
Wtokens, a single missing SWA block breaks the continuity.The Rule: An endpoint is valid if and only if the last
W_blocks(whereW_blocks = ceil(W / B)) are all valid in the SWA cache.Matching Process (Two Phases):
- Phase 1 (Scan): Traverse the prefix tree. Use a
swa_valid_in_windowcounter. Increment it for valid SWA blocks; reset it to 0 if an invalid block is encountered. Thelast_swa_valid_idxis updated only when the counter reachessliding_window_blocks. - Phase 2 (Registration): Once the furthest valid endpoint is found, register the blocks in the range
[win_start, last_swa_valid_idx)with theSWACacheManagerby callingSetReclaimable(fa_block_id, false)andIncrementHitCount(fa_block_id).
- Phase 1 (Scan): Traverse the prefix tree. Use a
SWA block eviction priority and strategy
mainWhen the SWA memory pool is exhausted and a new block must be allocated,
SWACacheManagerfollows a specific priority and eviction strategy to preserve high-value prefix cache data.Allocation Priority
- Highest Priority: Allocate directly from the idle pool using the
BlockAllocatorGroupInterface. If blocks are available here, no eviction occurs. - Lowest Priority: If the idle pool is empty, trigger eviction of reclaimable blocks where
is_content_fixed=true.
Eviction Selection (LFU + Prefix Position)
To maximize the computational savings of the prefix cache,
SWACacheManagerselects the "lowest value" block for eviction using a composite sort on reclaimable, content-fixed blocks:- Primary Key:
hit_count(Ascending) Blocks with fewer hits are considered less valuable and are evicted first (LFU strategy). - Secondary Key:
prefix_fa_block_num(Ascending) If hit counts are equal, the block with the smallerprefix_fa_block_num(the number of preceding FA blocks) is evicted first. A smaller index means the block provides less computational savings when reused.
Note: Blocks where
is_content_fixed=falseare never candidates for eviction; they are either actively being written to or are directly freed.- Highest Priority: Allocate directly from the idle pool using the
How Simple Router works in KsanaLLM
mainSimple Router is a FastAPI-based coordination service for KsanaLLM deployments. It manages the relationship between Prefill Nodes (which process prompts and generate KV cache) and Decode Nodes (which generate subsequent tokens autoregressively).
Core Workflow
- Node Registration: Nodes register via
/RegisterNodeand maintain status via/Heartbeat. - Pairing: The router tracks available prefill/decode pairs in a database. Any prefill node can be paired with any decode node (fully meshed topology).
- Request Handling: When a client sends a request to the router's proxy endpoints (
/generate,/v1/*, or/v2/*), the router:- Selects an available prefill/decode pair.
- Generates a unique
comm_id. - Forwards the request to both nodes simultaneously using custom headers (
kv-comm-group-key,kv-comm-request-id) to coordinate KV cache transfer. - Merges the streaming responses (prefill's first token + decode's subsequent tokens) and returns them to the client.
Cluster Topology
A cluster typically consists of $N$ prefill nodes and $M$ decode nodes. The router provides load balancing and fault tolerance by dynamically selecting pairs based on node health and availability recorded in the
node_infotable.- Node Registration: Nodes register via