# `offload_num_in_group` NPU 适配实现计划

> 赛道: CCF 开源创新大赛 vLLM Ascend 参数适配专项赛  
> 参数: `offload_num_in_group`  
> 状态: 已实现并通过单测、冒烟与 1.5B 参数矩阵测试  
> 日期: 2026-07-06

## 一、需求重述

`offload_num_in_group` 控制每个 decoder layer group 中有多少层进行权重 offload。它必须与 `offload_group_size` 配合使用，并满足：

```text
0 <= offload_num_in_group <= offload_group_size
```

当 `offload_num_in_group=0` 时，即使 `offload_group_size` 为正数，也必须保持 Noop。

## 二、参数语义

本实现选择每个 group 的末尾 N 层进行 offload：

```text
offset = layer_id % offload_group_size
offload if offset >= offload_group_size - offload_num_in_group
```

示例：

| num_layers | offload_group_size | offload_num_in_group | selected layers |
|---:|---:|---:|---|
| 12 | 4 | 0 | `()` |
| 12 | 4 | 1 | `(3, 7, 11)` |
| 12 | 4 | 2 | `(2, 3, 6, 7, 10, 11)` |
| 12 | 4 | 4 | `(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)` |

## 三、实现方案

`offload_num_in_group` 的校验和选择逻辑在 `selection.py` 中统一完成：

```python
if num_in_group < 0:
    raise ValueError("offload_num_in_group must be >= 0")
if num_in_group > group_size:
    raise ValueError(...)
```

`model_runner_v1.py` 在创建 offloader 前也做启动阶段校验，确保非法配置尽早失败：

```python
if num_in_group > group_size:
    raise ValueError(...)
```

## 四、涉及文件

| 文件 | 说明 |
|---|---|
| `vllm_ascend/model_executor/offloader/selection.py` | 实现每组 offload 层数选择与非法参数校验 |
| `vllm_ascend/model_executor/offloader/sync.py` | 同步 fallback 使用该参数选择 offload modules |
| `vllm_ascend/model_executor/offloader/prefetch.py` | 预取实现使用该参数选择 offload modules |
| `vllm_ascend/worker/model_runner_v1.py` | 读取参数并进行启动阶段校验 |
| `tests/model_executor/offloader/offload_num_in_group/test_param.py` | `offload_num_in_group` 参数级单测 |

## 五、兼容性

- `offload_num_in_group=0` 不启用 offload。
- `offload_num_in_group > offload_group_size` 明确报错，避免模糊行为。
- 参数只影响每组内 offload 层数，不改变 sync/prefetch 后端的执行接口。
