Skip to content

Commit 6f8e5e6

Browse files
committed
refactor: rename cuda torch ops library files.
Update the Python executor design document to match the current CUDA, TP, JSON, and decode graph implementation.
1 parent 14629cb commit 6f8e5e6

6 files changed

Lines changed: 105 additions & 47 deletions

File tree

Lines changed: 100 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,133 @@
1-
# Python 模型执行器 · 算子与交互架构
1+
# Python 模型执行器算子与交互架构
22

3-
## C++ ↔ Python 交互
3+
## 当前支持范围
44

5-
xLLM 进程通过 pybind11 嵌入式解释器调用 Python 模型。交互分三个阶段:
5+
Python 模型执行器通过 `--model_impl=python``py` 会在配置初始化时规范化为
6+
`python`)启用。目前只在 CUDA 构建中注册,内置 Python 模型仅包含 Qwen3
7+
dense。本文描述的是当前实现,不表示 NPU、MUSA 或其他模型已经可用。
8+
9+
`model_impl``python_model_path``python_graph_backend` 都可以通过命令行或
10+
JSON 配置设置,非默认生效值会写入启动配置转储。`python_model_path` 是包含
11+
`python` package 的目录;未设置时回退到 `XLLM_PYTHON_MODEL_PATH`,再由嵌入式
12+
解释器按原有 `sys.path` 查找。
13+
14+
## C++ 与 Python 交互
15+
16+
xLLM 服务进程通过 pybind11 嵌入 CPython,并在同一进程中调用 Python 模型。
17+
交互分为初始化、权重加载和逐步前向三个阶段;下面将初始化阶段中的 TP 通信组
18+
创建单独列出。
619

720
### 1. 初始化(`PyCausalLM` 构造)
821

22+
```text
23+
C++ ModelArgs/ParallelArgs -> PROPERTY 反射 -> PyDictVisitor -> py::dict config
924
```
10-
C++ ModelArgs/ParallelArgs → PROPERTY 反射 → PyDictVisitor → py::dict config
11-
```
1225

13-
传给 Python 模型的 `config` dict 包含:
14-
- 所有 `ModelArgs` 字段(`hidden_size``n_heads``n_kv_heads``n_layers``head_dim` 等)
15-
- 所有 `ParallelArgs` 字段(`tp_size``tp_rank`
16-
- `dtype`(字符串:`"bfloat16"` / `"float16"`
17-
- `device`(字符串:`"cuda:0"` / `"npu:0"`
18-
- `python_graph_backend``max_seqs_per_batch`(decode graph 捕获上限)
26+
`ensure_python_interpreter()` 先保证 `torch.ops.xllm_ops.*` 的 C++ 静态注册不会被
27+
链接器裁剪,再初始化解释器并导入 `python` package。传给 Python 模型的
28+
`config` dict 包含:
29+
30+
- 可反射的 `ModelArgs``ParallelArgs` 字段;
31+
- 根据实际 TP group 得到的 `tp_size``tp_rank`
32+
- `dtype` 和当前 CUDA `device`
33+
- `python_graph_backend`
34+
- 调度器的 `max_seqs_per_batch`,作为 decode cudagraph 的最大 batch 上限。
35+
36+
Python 侧通过 `registry.get_model_class(model_type)` 查找模型类并用该 dict 构造。
37+
当前 registry 只注册 `Qwen3ForCausalLM`/`qwen3`
38+
39+
### 2. TP 通信组初始化
1940

20-
Python 模型类通过 `registry.get_model_class(model_type)` 查找,用 `config` dict 构造。
41+
`tp_size > 1` 时,Python executor 不复用或包装 C++ `ProcessGroup`。C++ 只把
42+
当前 TP 子组对应的 rendezvous host/port、rank 和 world size 传给
43+
`python.ops.init_tp_group()`;Python 使用 `torch.distributed.TCPStore`
44+
`ProcessGroupNCCL` 创建独立的 PyTorch TP group。
2145

22-
### 2. 权重加载(`load_model`
46+
通信组按 CUDA device 缓存。TP 层通过 `torch.ops.xllm_ops.all_reduce`
47+
`torch.ops.xllm_ops.all_gather` 调用 `torch.distributed`;若 TP>1 时尚未初始化
48+
group,算子直接报错,不会静默返回局部张量。TP=1 时各层不调用通信算子。
2349

24-
C++ `StateDict`(safetensors mmap)通过 `PYBIND11_EMBEDDED_MODULE(xllm_weight_loader)` 暴露为 Python 对象,接口:
25-
- `state_dict.keys() → list[str]`
26-
- `state_dict.has(name) → bool`
27-
- `state_dict.get_tensor(name) → torch.Tensor`(零拷贝,直接引用 mmap 内存)
50+
### 3. 权重加载(`load_model`
2851

29-
Python 模型的 `load_weights(state_dicts, tp_rank, tp_size)` 负责按 TP 切分加载。
52+
C++ `StateDict` 通过 `PYBIND11_EMBEDDED_MODULE(xllm_weight_loader)` 暴露为
53+
Python 对象,接口为:
3054

31-
### 3. 前向推理(每步调用)
55+
- `state_dict.keys() -> list[str]`
56+
- `state_dict.has(name) -> bool`
57+
- `state_dict.get_tensor(name) -> torch.Tensor`
3258

33-
C++ 每步构造参数并调用 `py_model_.forward(tokens, positions, attention_metadata, kv_caches)`
59+
`get_tensor()` 直接返回 C++ `StateDict` 持有的 tensor,不在 pybind bridge 中
60+
额外复制。Python 模型的 `load_weights(state_dicts, tp_rank, tp_size)` 负责权重
61+
变换、TP 切分和复制到目标参数。
62+
63+
### 4. 前向推理(每步调用)
64+
65+
C++ 每步调用
66+
`py_model_.forward(tokens, positions, attention_metadata_dict, kv_caches)`
3467

3568
| 参数 | 类型 | 说明 |
3669
|------|------|------|
3770
| `tokens` | `Tensor[int32]` | 当前步 token ids |
3871
| `positions` | `Tensor[int32]` | 位置编码 |
39-
| `attention_metadata` | `dict` | attention 元数据(见下) |
40-
| `kv_caches` | `list[tuple[Tensor, Tensor]]` | 每层 (k_cache, v_cache) |
72+
| `attention_metadata_dict` | `dict` | attention 元数据 |
73+
| `kv_caches` | `list[tuple[Tensor, Tensor]]` | 每层 `(k_cache, v_cache)` |
4174

42-
`attention_metadata` dict 字段
75+
`attention_metadata_dict` 包含以下字段
4376

4477
| 字段 | 含义 |
4578
|------|------|
4679
| `slot_mapping` | 新 token 写入 cache 的 slot 位置 |
4780
| `paged_kv_indptr` | paged KV 的 CSR indptr |
4881
| `paged_kv_indices` | paged KV 的物理 block indices |
4982
| `paged_kv_last_page_len` | 每个序列最后一页的有效 token 数 |
50-
| `is_prefill` | 是否为 prefill 阶段 |
83+
| `is_prefill` | 是否为普通 prefill |
5184
| `is_chunked_prefill` | 是否为 chunked prefill |
52-
| `enable_cuda_graph` | 是否在 graph capture/replay 中 |
53-
| `q_cu_seq_lens` | query 累积序列长度(prefill 时) |
54-
| `kv_cu_seq_lens` | KV 累积序列长度(prefill 时) |
55-
| `qo_indptr` | chunked prefill 的 query-output indptr |
85+
| `enable_cuda_graph` | C++ attention metadata 中的 graph 状态 |
86+
| `use_tensor_core` | 当前固定为 `false` |
87+
| `q_cu_seq_lens` | 普通 prefill 的 query 累积长度,可选 |
88+
| `kv_cu_seq_lens` | 普通 prefill 的 KV 累积长度,可选 |
89+
| `qo_indptr` | chunked prefill 的 query-output indptr,可选 |
5690

57-
返回值:`torch.Tensor`(hidden_states),C++ 侧再调 `compute_logits` 得到 logits。
91+
Python runner 负责 attention plan、forward context 和 graph 调度。模型返回
92+
`hidden_states`,C++ 随后调用 Python `compute_logits()``selected_idxes` 存在时
93+
先选择需要计算 logits 的 token。
5894

59-
## 算子分类
95+
## Graph 执行模式
6096

61-
| 类别 | 注册方式 | 位置 |
62-
|------|----------|------|
63-
| 计算 op (`rms_norm` / `fused_add_rms_norm` / `silu_and_mul` / `fused_qk_norm_rope` / `reshape_paged_cache`) | C++ `TORCH_LIBRARY(xllm_ops)` schema + `TORCH_LIBRARY_IMPL(CUDA)` | `core/kernels/cuda/xllm_ops_library.cpp` |
64-
| 通信 op (`all_reduce` / `all_gather`) | Python `torch.library.custom_op``torch.distributed` | `python/ops/collectives.py` |
65-
| Attention (`batch_prefill` / `batch_decode`) | flashinfer Python API 直接调用 | `python/layers/attention.py` |
97+
`python_graph_backend` 的行为如下:
6698

67-
设计约束:
68-
- `models/``layers/` 只调 `ops.*` 接口,不含设备分支。
69-
- 计算 op 的 `register_fake` 设备无关,torch.compile 图中复用。
70-
- Attention 不走 `torch.ops`,由 Python 层直接管理 plan/workspace 生命周期。
99+
| 配置值 | 行为 |
100+
|--------|------|
101+
| `off`、空字符串、`none``0` | 全部 eager |
102+
| `cudagraphs` | prefill/chunked prefill eager;decode 使用完整 CUDA graph |
103+
| 其他值 | 作为 `torch.compile(model, backend=...)` 的 backend |
71104

72-
## 风险
105+
`cudagraphs` 按 batch bucket 捕获 decode graph:`1/2/4/8`,之后按 16 向上取整,
106+
不超过 `max_seqs_per_batch`。该上限来自调度配置,不再有独立的
107+
`python_graph_max_batch` 参数。
108+
109+
每个 bucket 使用固定地址的 input、position 和 attention metadata buffer。
110+
padding 序列使用 block manager 预留的 block 0;`paged_kv_indices` 静态 buffer
111+
在真实 KV block 数之外额外预留 `batch_pad` 个 index,避免 KV cache 接近满载时
112+
dummy metadata 越界。FlashInfer plan 在 capture/replay 外执行,并与 graph 使用
113+
同一条专用 CUDA stream。
114+
115+
## 算子分类
73116

74-
- in-place op(`fused_add_rms_norm` / `fused_qk_norm_rope`)的非 CUDA 实现必须保持 `(a!)` mutating 语义。
75-
- 跨硬件不保证 bit-identical,对齐标准为贪心解码 token 序列一致。
117+
| 类别 | 注册/调用方式 | 位置 |
118+
|------|---------------|------|
119+
| CUDA 计算 op(`rms_norm``fused_add_rms_norm``silu_and_mul``fused_qk_norm_rope``reshape_paged_cache`| C++ `TORCH_LIBRARY(xllm_ops)` schema + `TORCH_LIBRARY_IMPL(CUDA)` | `core/kernels/cuda/cuda_ops_library.cpp` |
120+
| TP 通信 op(`all_reduce``all_gather`| Python `torch.library.custom_op` + `torch.distributed` NCCL group | `python/ops/collectives.py` |
121+
| Attention(prefill/decode) | 直接调用 FlashInfer Python API | `python/layers/attention.py` |
122+
123+
约束如下:
124+
125+
- `models/``layers/` 通过 `python.ops` 调用计算和通信算子;设备实现由当前
126+
CUDA 注册或 Python collective group 决定。
127+
- C++ 注册的计算 op 在 Python 中补充 `register_fake`,供 `torch.compile` 使用。
128+
- Attention 不注册为 `torch.ops`,由 Python 层管理 FlashInfer wrapper、plan 和
129+
workspace 生命周期。
130+
- in-place op(`fused_add_rms_norm``fused_qk_norm_rope`)必须保持 schema 中的
131+
mutating 语义。
132+
- TP=1/TP=2 的正确性验收以相同请求的贪心 token 序列与 native executor 一致为
133+
基准,不要求不同硬件间 bit-identical。

tests/core/kernels/cuda/xllm_ops_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ limitations under the License.
3232

3333
#include <filesystem>
3434

35-
#include "core/kernels/cuda/xllm_ops_library.h"
35+
#include "core/kernels/cuda/cuda_ops_library.h"
3636

3737
namespace py = pybind11;
3838

xllm/core/kernels/cuda/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ set(CUDA_HEADER_FILES
3737
topk_last_dim.cuh
3838
type_convert.cuh
3939
utils.h
40-
xllm_ops_library.h
40+
cuda_ops_library.h
4141
device_utils.cuh
4242
xattention/xattention_ops_api.h
4343
moe/moe_topk.cuh
@@ -120,7 +120,7 @@ set(CUDA_SOURCE_FILES
120120
rope.cu
121121
random_sample.cpp
122122
utils.cpp
123-
xllm_ops_library.cpp
123+
cuda_ops_library.cpp
124124
xattention/beam_search.cpp
125125
xattention/cache_select.cu
126126
xattention/decoder_reshape_and_cache.cu

xllm/core/kernels/cuda/xllm_ops_library.cpp renamed to xllm/core/kernels/cuda/cuda_ops_library.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ limitations under the License.
2323
// kernels are exposed here. Attention is handled by the flashinfer Python
2424
// package directly (see layers/attention.py).
2525

26-
#include "core/kernels/cuda/xllm_ops_library.h"
26+
#include "core/kernels/cuda/cuda_ops_library.h"
2727

2828
#include <glog/logging.h>
2929
#include <torch/library.h>

xllm/core/kernels/xllm_torch_ops.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#pragma once
33

44
#if defined(USE_CUDA) || defined(USE_MUSA)
5-
#include "core/kernels/cuda/xllm_ops_library.h"
5+
#include "core/kernels/cuda/cuda_ops_library.h"
66
#endif
77

88
namespace xllm {

0 commit comments

Comments
 (0)