已合并
feat(model): 按 RFC #47 重构推理知识并增加可选 Scope 检索 #143
汪超创建于 15 天前
feat(model): 按 RFC #47 重构推理知识并增加可选 Scope 检索 #143
已合并
共 384 个文件变更+15395-6270
| @@ -37,7 +37,9 @@ python3 <skill-dir>/scripts/knowledge_ingest.py --knowledge-root <repo> create \ | |||
| 37 | 37 | ||
| 38 | 审阅 dry-run 后移除 `--dry-run`。`create` 子命令以排他原子写入创建候选卡,不覆盖已有文件,也不更新 index 或日志。默认 `draft`;只有来源、平台、正文与 Profile 门禁均满足后才显式使用 `--status stable`。 | 38 | 审阅 dry-run 后移除 `--dry-run`。`create` 子命令以排他原子写入创建候选卡,不覆盖已有文件,也不更新 index 或日志。默认 `draft`;只有来源、平台、正文与 Profile 门禁均满足后才显式使用 `--status stable`。 |
| 39 | 39 | ||
| 40 | -候选卡可在 Profile 允许时声明 `scope`,由同版本 Contract 校验。只提取来源支持且有过滤价值的条件,不要求每张卡填写或补齐维度;按领域选取注册 Schema。字段及准入边界见[路径与 Frontmatter Profile](references/frontmatter-profiles.md),不把声明存在当成 Query 已启用过滤。 | 40 | +Scope 保持可选,不要求每张卡填写或达到填写率。只提取来源支持且有过滤价值的条件;已确认部分即可声明,不等待全部维度齐备,也不为补齐字段写 `any`。先按领域选择已注册 Schema,不把模型推理维度套到其他领域;特殊匹配由受审查的共享 Contract 处理器扩展,卡片不保存可执行规则。卡片声明 Scope 不代表查询已启用过滤,是否过滤以 Query 的显式参数为准。 |
| 41 | + | ||
| 42 | +需要声明场景适用条件时,可用 `--scope-file <JSON/YAML>` 读取 Scope 对象;字段、维度和适用范围由同一 Contract 校验,卡片只用规范值和来源支持的前提。模型推理卡片可声明有来源依据的 `parallelism.none_of`,仍拒绝查询专用的 `model_features.none_of`,不从来源未提及推断否定。参数与 Profile 说明见[路径与 Frontmatter Profile](references/frontmatter-profiles.md)。 | ||
| 41 | 43 | ||
| 42 | ## 收尾门禁 | 44 | ## 收尾门禁 |
| 43 | 45 | ||
| @@ -24,8 +24,6 @@ | |||
| 24 | 24 | ||
| 25 | `scope` 可选且允许部分声明,只填写来源支持并有过滤价值的条件;未知维度省略,不以 `any` 或否定条件补齐。不同领域使用各自注册的 Schema、适用范围和维度语义,共享形状和三态协议。具体语法见治理 [Frontmatter](../../../../governance/specs/frontmatter.md#场景适用条件scope) 与 [机器契约](../../../../governance/specs/schemas.md),不在 producer 中维护第二份词表。 | 25 | `scope` 可选且允许部分声明,只填写来源支持并有过滤价值的条件;未知维度省略,不以 `any` 或否定条件补齐。不同领域使用各自注册的 Schema、适用范围和维度语义,共享形状和三态协议。具体语法见治理 [Frontmatter](../../../../governance/specs/frontmatter.md#场景适用条件scope) 与 [机器契约](../../../../governance/specs/schemas.md),不在 producer 中维护第二份词表。 |
| 26 | 26 | ||
| 27 | -当前 `create` 参数保持原样,不提供场景 Scope 选项;审阅后人工补充的声明仍由完整候选校验、Lint、索引及构图复用同一 Contract 验证。 | ||
| 28 | - | ||
| 29 | ## Trust | 27 | ## Trust |
| 30 | 28 | ||
| 31 | 没有 `verified` 表示 unverified。只有真实执行验证或人工复核后,才记录带 actor 和时间的 OKF event: | 29 | 没有 `verified` 表示 unverified。只有真实执行验证或人工复核后,才记录带 actor 和时间的 OKF event: |
| @@ -44,6 +42,7 @@ Actor 使用 `human:<id>`、`process:<id>` 或 `<producer>/<version>`。不得 | |||
| 44 | - Concept 与索引导航只能指向同一 `knowledge/` Bundle;知识卡还可引用 Registry 登记的 `kind: git` 固定提交源码文件作为本地来源链接,不形成图谱节点。`sources[].resource` 相对于知识仓根,正文链接相对于当前卡目录。 | 42 | - Concept 与索引导航只能指向同一 `knowledge/` Bundle;知识卡还可引用 Registry 登记的 `kind: git` 固定提交源码文件作为本地来源链接,不形成图谱节点。`sources[].resource` 相对于知识仓根,正文链接相对于当前卡目录。 |
| 45 | - `interoperability` 只能位于同领域 `shared`,并声明不同的 source/target technology。 | 43 | - `interoperability` 只能位于同领域 `shared`,并声明不同的 source/target technology。 |
| 46 | - `concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可以增加小写 `snake_case` 主题 section。 | 44 | - `concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可以增加小写 `snake_case` 主题 section。 |
| 45 | +- 已停用 section 由 Registry 的 `paths.forbidden_sections` 按 domain/route/Profile 声明,卡片和索引共用检查;模型推理的 Guide 与 Optimization 不再使用 `experience` 层。具体职责与当前任务目录见治理路径 Spec。 | ||
| 47 | - operator 使用 `operators/<call-mode>/<family>/<category>/<operator>.md`;`call-mode` 当前为 `aclnn` 或 `direct`。 | 46 | - operator 使用 `operators/<call-mode>/<family>/<category>/<operator>.md`;`call-mode` 当前为 `aclnn` 或 `direct`。 |
| 48 | - runbook 使用 `runbooks/<area>/<runbook>.md`。 | 47 | - runbook 使用 `runbooks/<area>/<runbook>.md`。 |
| 49 | - call mode、family、family 下的 category 和 area 的合法值从 `registries.yaml` 的 `paths` 分区读取。 | 48 | - call mode、family、family 下的 category 和 area 的合法值从 `registries.yaml` 的 `paths` 分区读取。 |
| @@ -54,4 +53,6 @@ Actor 使用 `human:<id>`、`process:<id>` 或 `<producer>/<version>`。不得 | |||
| 54 | - `--source` 参数写入 OKF `sources[].resource`;第一项自动带 `role: primary`。来源可以是 HTTP(S),也可以是 Registry 登记的文档归档或固定提交 Git 本地路径。 | 53 | - `--source` 参数写入 OKF `sources[].resource`;第一项自动带 `role: primary`。来源可以是 HTTP(S),也可以是 Registry 登记的文档归档或固定提交 Git 本地路径。 |
| 55 | - `--verified-by` 与 `--verified-at` 必须同时出现,当前一次创建一个 event;多个 event 可在审阅后人工补充。 | 54 | - `--verified-by` 与 `--verified-at` 必须同时出现,当前一次创建一个 event;多个 event 可在审阅后人工补充。 |
| 56 | - `--dry-run` 输出完整 Markdown,但不创建目录、卡片、索引或日志。 | 55 | - `--dry-run` 输出完整 Markdown,但不创建目录、卡片、索引或日志。 |
| 56 | +- `--scope-file` 读取 JSON/YAML 格式的 Scope 对象本身,执行统一 Contract 校验后写入可选 Frontmatter `scope`。不要求每张卡填写;来源支持且有过滤价值的部分条件即可声明,未知维度省略,不为补齐字段写 `any`。Schema、维度和匹配方式按领域注册,不套用模型字段。 | ||
| 57 | +- 模型推理卡片可用 `parallelism.none_of` 声明来源明确排除的并行方式,不接受查询专用 `model_features.none_of`,不使用别名或自动推断条件。集合的 `all_of` 与 `none_of` 至少一项非空、可同时使用但不得相交;未提及不表示不存在。 | ||
| 57 | - 非 dry-run 只创建不存在的卡片,不覆盖或更新已有文件。 | 58 | - 非 dry-run 只创建不存在的卡片,不覆盖或更新已有文件。 |
| @@ -8,6 +8,7 @@ | |||
| 8 | - `platforms` 必须与正文和固定来源证明的实际范围完全一致;未知平台使用空列表并保持 `draft`,不得根据偶然平台词、架构演进描述、章节内局部能力限制或某工作模式限制猜测整卡平台。没有产品支持表或明确以本卡、本接口、本样例等为主语的整卡范围句时保持未知。 | 8 | - `platforms` 必须与正文和固定来源证明的实际范围完全一致;未知平台使用空列表并保持 `draft`,不得根据偶然平台词、架构演进描述、章节内局部能力限制或某工作模式限制猜测整卡平台。没有产品支持表或明确以本卡、本接口、本样例等为主语的整卡范围句时保持未知。 |
| 9 | - 平台无关只在定义本身不会随硬件变化时使用 `agnostic`,不能与具体平台混用。 | 9 | - 平台无关只在定义本身不会随硬件变化时使用 `agnostic`,不能与具体平台混用。 |
| 10 | - 来源固定、lint 通过和单次成功都不自动生成 `verified` 事件。 | 10 | - 来源固定、lint 通过和单次成功都不自动生成 `verified` 事件。 |
| 11 | +- 改写标题或摘要时保留主题的稳定名称、英文名、缩写和关键实现标识。Profile 允许时可用 `aliases` 保存稳定检索名称;不允许时在标题、摘要或正文自然保留,不临时扩展字段。不要把含收益承诺的旧任务长句整段搬进别名;用原名称和实际短查询分别验证召回,避免迁移后规范入口难以查找。 | ||
| 11 | 12 | ||
| 12 | 可选 `scope` 只记录来源支持且对过滤有帮助的条件;缺少 Scope 或缺少维度不意味着普遍适用。不得按标题、移动目录或检索相关性扩大适用范围,也不为提升填写率补造条件。不同领域的约束使用各自注册的 Schema,不套用模型推理维度。 | 13 | 可选 `scope` 只记录来源支持且对过滤有帮助的条件;缺少 Scope 或缺少维度不意味着普遍适用。不得按标题、移动目录或检索相关性扩大适用范围,也不为提升填写率补造条件。不同领域的约束使用各自注册的 Schema,不套用模型推理维度。 |
| 13 | 14 | ||
| @@ -26,3 +27,15 @@ | |||
| 26 | - 一概念一 canonical 卡;交叉引用已有卡,不复制第二份同义内容。 | 27 | - 一概念一 canonical 卡;交叉引用已有卡,不复制第二份同义内容。 |
| 27 | - 按需生成的 API 到 Operator 引用建议只接受可复验的明确调用:优先来自卡片声明的固定版本源码;仅有正文证据时单独标注,不宣称已在源码调用。完整同名集合的唯一归属必须早于平台与参数数量过滤,泛化符号与已审核否决 pair 不得绕过。源码范围、词法排除、证据形状及展示上限统一遵循[本地图谱](local-graphs.md);限额只影响建议展示,不限制正文链接或图谱边,不抹去完整审计。 | 28 | - 按需生成的 API 到 Operator 引用建议只接受可复验的明确调用:优先来自卡片声明的固定版本源码;仅有正文证据时单独标注,不宣称已在源码调用。完整同名集合的唯一归属必须早于平台与参数数量过滤,泛化符号与已审核否决 pair 不得绕过。源码范围、词法排除、证据形状及展示上限统一遵循[本地图谱](local-graphs.md);限额只影响建议展示,不限制正文链接或图谱边,不抹去完整审计。 |
| 28 | - 完整样例工程、算子模板编译、轨迹进化等需要专用 producer;当前未迁移时明确 defer。 | 29 | - 完整样例工程、算子模板编译、轨迹进化等需要专用 producer;当前未迁移时明确 defer。 |
| 30 | + | ||
| 31 | +## 模型推理卡片组织 | ||
| 32 | + | ||
| 33 | +先检索同主题候选并读完整正文和来源,明确 create/update/merge/split/deprecate/defer。Concept 解释机制,Optimization 拥有优化决策,Guide 串联任务,Case 只补特定条件下的实施差异和证据;其他卡通过链接复用规则。反模式已有完整“症状—探针—修复—验证”链路时由 Runbook 承载。不要为了配齐类型复制正文;目录迁移和归并先形成逐卡清单,不能按标题自动合并。 | ||
| 34 | + | ||
| 35 | +正文按职责覆盖以下内容,可保留语义等价的小节,不能用标题齐全代替内容或补造缺失证据: | ||
| 36 | + | ||
| 37 | +- Optimization:适用场景、问题信号、优化规则、机制、实施要点、收益、代价和风险、回退方式、验证方法、适用边界、相关经验、Evidence。 | ||
| 38 | +- Runbook:症状、快速检查、诊断路径、根因、修复、验证、回退和残留风险、Evidence;轨迹型知识继续遵守对应 producer 的形态与证据准入。 | ||
| 39 | +- Benchmark Observation:Workload、Hardware、Software、Baseline、Variant、Metric Definition、Repetition and Variance、Result、Limitations、Evidence。记录 batch、序列长度、并发、卡数/并行、软件版本、warmup/重复次数、指标定义、受控对比和独立复测情况;来源未提供时明确未知,不将单次或仓库自报数据泛化成性能保证。 | ||
| 40 | + | ||
| 41 | +稳定卡需要明确适用范围、失效边界、验证信号和相应证据;`scope` 只填来源支持的条件,`verified` 仍只记录真实复核事件。Scope 未标注不等于普遍适用,Case 和观测数据也不因关联规则有更宽范围而获得同等适用性。 | ||
| @@ -181,6 +181,10 @@ def create(args: argparse.Namespace) -> int: | |||
| 181 | metadata["target_technology"] = args.target_technology | 181 | metadata["target_technology"] = args.target_technology |
| 182 | if args.replaced_by: | 182 | if args.replaced_by: |
| 183 | metadata["replaced_by"] = args.replaced_by | 183 | metadata["replaced_by"] = args.replaced_by |
| 184 | + if getattr(args, "scope_file", None): | ||
| 185 | + metadata["scope"] = contract.parse_scope( | ||
| 186 | + Path(args.scope_file).read_text(encoding="utf-8") | ||
| 187 | + ) | ||
| 184 | 188 | ||
| 185 | issues = contract.validate_card(relative, metadata, body) | 189 | issues = contract.validate_card(relative, metadata, body) |
| 186 | if issues: | 190 | if issues: |
| @@ -333,6 +337,7 @@ def parser() -> argparse.ArgumentParser: | |||
| 333 | command.add_argument("--source-technology") | 337 | command.add_argument("--source-technology") |
| 334 | command.add_argument("--target-technology") | 338 | command.add_argument("--target-technology") |
| 335 | command.add_argument("--replaced-by") | 339 | command.add_argument("--replaced-by") |
| 340 | + command.add_argument("--scope-file", help="Optional canonical card Scope in a JSON/YAML file") | ||
| 336 | command.add_argument("--body-file", required=True) | 341 | command.add_argument("--body-file", required=True) |
| 337 | command.add_argument("--dry-run", action="store_true") | 342 | command.add_argument("--dry-run", action="store_true") |
| 338 | command.set_defaults(handler=create) | 343 | command.set_defaults(handler=create) |
| @@ -8,9 +8,9 @@ | |||
| 8 | 8 | ||
| 9 | | ID | 等级 | 检查内容 | | 9 | | ID | 等级 | 检查内容 | |
| 10 | |---|---|---| | 10 | |---|---|---| |
| 11 | -| `path` | P0 | domain-route、Profile、内容型 section、Operator/Runbook 附加层级和小写 snake_case 卡名合法 | | 11 | +| `path` | P0 | domain-route、Profile、内容型 section、Registry 声明的已停用 section、Operator/Runbook 附加层级和小写 snake_case 卡名合法 | |
| 12 | | `profile` | P0 | YAML mapping 无重复 key;JSON Schema、公共/Profile 必选字段、非空标题摘要和路径到 OKF type 的映射一致;缺失与空白分别报告 | | 12 | | `profile` | P0 | YAML mapping 无重复 key;JSON Schema、公共/Profile 必选字段、非空标题摘要和路径到 OKF type 的映射一致;缺失与空白分别报告 | |
| 13 | -| `scope` | P0 | 可选声明的 namespace、Profile/路径绑定、分支、维度和值符合 Schema、Registry 与 Contract;未知或缺失条件不补全,不允许卡片使用查询专用否定 | | 13 | +| `scope` | P0 | 可选场景 Scope 的 namespace、Profile/路径绑定、分支、维度、类型和值符合 Schema、Registry 与 Contract;缺失条件不补全,拒绝查询专用否定及混合通配值 | |
| 14 | | `body` | P0 | 正文非空且围栏外至少有一个 H1;除 Registry 文档归档主来源的 API/Guide 受控转换可保留来源中的多个 H1 外,其余卡恰有一个 H1;摘要不含发布占位,正文无 Git 冲突标记或压平到单行的 Markdown 表格 | | 14 | | `body` | P0 | 正文非空且围栏外至少有一个 H1;除 Registry 文档归档主来源的 API/Guide 受控转换可保留来源中的多个 H1 外,其余卡恰有一个 H1;摘要不含发布占位,正文无 Git 冲突标记或压平到单行的 Markdown 表格 | |
| 15 | | `images` | P0 | Markdown/HTML 图片使用含 40 位 commit 和具体文件路径的 HTTPS Git raw URL,忽略代码中的伪图片 | | 15 | | `images` | P0 | Markdown/HTML 图片使用含 40 位 commit 和具体文件路径的 HTTPS Git raw URL,忽略代码中的伪图片 | |
| 16 | | `tags` | P0 | 标签受控、无重复,且至少包含一个 task 和一个 topic | | 16 | | `tags` | P0 | 标签受控、无重复,且至少包含一个 task 和一个 topic | |
| @@ -12,6 +12,8 @@ description: 检索 cannbot-knowledge 中受治理的 CANN、算子、图、模 | |||
| 12 | - 明确一个要回答的判断(decision),保留目标实体、准确 API 名或首发错误信号。复杂任务拆分判断,不把实现、调试和性能问题塞入一条 query。 | 12 | - 明确一个要回答的判断(decision),保留目标实体、准确 API 名或首发错误信号。复杂任务拆分判断,不把实现、调试和性能问题塞入一条 query。 |
| 13 | - 先确定**任务目标平台**,不是默认采用 Agent 所在机器。只有型号、SocVersion、架构宏或配置时,读取[平台识别](references/platform-identification.md);存在歧义或冲突时先确认,不猜测或移除平台条件。安装项目可在 `.cannbot/knowledge.env` 配置 `CANNBOT_KNOWLEDGE_PLATFORM` 作为缺省平台,省略 `--platform` 时由脚本回退到该值;任务目标与缺省值不一致时必须显式传 `--platform`。 | 13 | - 先确定**任务目标平台**,不是默认采用 Agent 所在机器。只有型号、SocVersion、架构宏或配置时,读取[平台识别](references/platform-identification.md);存在歧义或冲突时先确认,不猜测或移除平台条件。安装项目可在 `.cannbot/knowledge.env` 配置 `CANNBOT_KNOWLEDGE_PLATFORM` 作为缺省平台,省略 `--platform` 时由脚本回退到该值;任务目标与缺省值不一致时必须显式传 `--platform`。 |
| 14 | - 明确必要的 domain、technology 与路径 Profile。只增加已知且有助区分的过滤项,不为了“最窄”叠满 task/tag;多个 tag 和 platform 都是 AND。共同适用知识用多平台过滤,平台差异分开查询。 | 14 | - 明确必要的 domain、technology 与路径 Profile。只增加已知且有助区分的过滤项,不为了“最窄”叠满 task/tag;多个 tag 和 platform 都是 AND。共同适用知识用多平台过滤,平台差异分开查询。 |
| 15 | +- Scope 可选,按任务领域选择已注册 Schema,只填写已确认且有过滤价值的部分条件,不要求卡片或查询补齐所有维度,不套用其他领域字段;推断仅作文本线索,不静默构造硬过滤。Scope 只排除明确冲突,未知项仍可相关;选卡时核对返回的分支和待确认前提,不能把召回或 `match` 当成收益已验证。语法、否定条件的卡片/查询准入与返回解释见 CLI。 | ||
| 16 | +- 从自然语言整理 Scope 时按[查询与证据工作流](references/query-workflows.md#自然语言到查询-scope)区分明确条件、推断和未知;必要时询问会影响结论的缺失前提,再决定是否提交结构化过滤。 | ||
| 15 | - 首次进入一个知识仓时运行 `discover`,区分真实覆盖与注册/占位目录;同一轮任务、同一知识根且索引指纹未变时复用结果。它的各维度计数不证明某个组合已有覆盖。 | 17 | - 首次进入一个知识仓时运行 `discover`,区分真实覆盖与注册/占位目录;同一轮任务、同一知识根且索引指纹未变时复用结果。它的各维度计数不证明某个组合已有覆盖。 |
| 16 | 18 | ||
| 17 | 按 [CLI](references/cli.md) 定位知识根、参数与输出。索引缺失、损坏或 stale 时停止,交由知识仓维护者或项目安装器准备;Query 不扫描全库兜底、不静默重建,也不自行下载资源。 | 19 | 按 [CLI](references/cli.md) 定位知识根、参数与输出。索引缺失、损坏或 stale 时停止,交由知识仓维护者或项目安装器准备;Query 不扫描全库兜底、不静默重建,也不自行下载资源。 |
| @@ -64,6 +66,8 @@ python3 <skill-dir>/scripts/knowledge_query.py search \ | |||
| 64 | 4. 知识库链接是有效入口,直接沿链接打开,不需要先运行链接检查。普通相对链接以当前卡目录解析,前导 `/` 的知识链接以 `knowledge/` Bundle 根解析,`#锚点` 指向当前卡;跳到 `index.md` 后按需选择真实卡。Agent 仍需判断新卡内容的平台、版本和适用性。链接及其说明不替代目标卡的证据;列出的算子不表示完整调用集合。 | 66 | 4. 知识库链接是有效入口,直接沿链接打开,不需要先运行链接检查。普通相对链接以当前卡目录解析,前导 `/` 的知识链接以 `knowledge/` Bundle 根解析,`#锚点` 指向当前卡;跳到 `index.md` 后按需选择真实卡。Agent 仍需判断新卡内容的平台、版本和适用性。链接及其说明不替代目标卡的证据;列出的算子不表示完整调用集合。 |
| 65 | 5. 记录已读卡和未解决项以避免循环;读过的适用证据足够回答时停止。只看过的摘要、打不开的源码、未读头文件不能写成已核验。 | 67 | 5. 记录已读卡和未解决项以避免循环;读过的适用证据足够回答时停止。只看过的摘要、打不开的源码、未读头文件不能写成已核验。 |
| 66 | 68 | ||
| 69 | +形成结论时按[结论范围与完成边界](references/query-workflows.md#从相关结果到结论)区分机制解释、方案选择与实际部署;先剔除当前 decision 无关的候选,再按[未知原因与补证顺序](references/query-workflows.md#未知原因与补证顺序)处理相关项的缺口。局部线索不足以完成整个主题时,沿规范入口补读或定向补搜,再明确保留的缺口。 | ||
| 70 | + | ||
| 67 | 不同本地路径的基准和缺失处理见 [CLI](references/cli.md)。普通读卡不要求 SDK 存在;只有依赖源码才能回答的部分才需要继续定位源码,不为一个已被正文充分回答的问题扫描整个 SDK。 | 71 | 不同本地路径的基准和缺失处理见 [CLI](references/cli.md)。普通读卡不要求 SDK 存在;只有依赖源码才能回答的部分才需要继续定位源码,不为一个已被正文充分回答的问题扫描整个 SDK。 |
| 68 | 72 | ||
| 69 | ## 5. 零命中、补搜与停止 | 73 | ## 5. 零命中、补搜与停止 |
| @@ -9,6 +9,8 @@ | |||
| 9 | 3. 把搬运、计算、同步、片上容量、并行度、模板和精度路线拆成独立 decision;每轮只改变一个证据角度。 | 9 | 3. 把搬运、计算、同步、片上容量、并行度、模板和精度路线拆成独立 decision;每轮只改变一个证据角度。 |
| 10 | 4. 对候选记录适用 workload、预期机制、风险、需要冻结的指标和回退条件。 | 10 | 4. 对候选记录适用 workload、预期机制、风险、需要冻结的指标和回退条件。 |
| 11 | 11 | ||
| 12 | +同一问题包含多种优化机制时,分别阅读规范规则和对应实现,不把某案例的核数、同步 API 或平台条件转给另一个案例。候选缺少关键前提时,按[未知原因与补证顺序](../../references/query-workflows.md#未知原因与补证顺序)区分任务输入不足和来源证据不足,只询问会改变当前选择的条件。 | ||
| 13 | + | ||
| 12 | ## 补搜与使用 | 14 | ## 补搜与使用 |
| 13 | 15 | ||
| 14 | 卡片中的优化只形成可测假设。未冻结基线、测量协议和正确性门禁时不宣称收益;每个实验结果通过 outcome manifest 绑定实际日志或报告。 | 16 | 卡片中的优化只形成可测假设。未冻结基线、测量协议和正确性门禁时不宣称收益;每个实验结果通过 outcome manifest 绑定实际日志或报告。 |
| @@ -56,6 +56,7 @@ python3 <skill-dir>/scripts/knowledge_query.py --knowledge-root <repo> search \ | |||
| 56 | [--task implementation] [--tag topic.data_movement] \ | 56 | [--task implementation] [--tag topic.data_movement] \ |
| 57 | --platform a2 [--include-shared] \ | 57 | --platform a2 [--include-shared] \ |
| 58 | [--source-technology triton] [--target-technology ascendc] \ | 58 | [--source-technology triton] [--target-technology ascendc] \ |
| 59 | + [--scope-file <confirmed-scope.yaml> | --scope-json '<confirmed-scope-object>'] \ | ||
| 59 | [--status stable|draft|deprecated|all] [--match text|literal] \ | 60 | [--status stable|draft|deprecated|all] [--match text|literal] \ |
| 60 | [--decision <id>] [-k 10] [--json|--text] [--receipt <new-json>] | 61 | [--decision <id>] [-k 10] [--json|--text] [--receipt <new-json>] |
| 61 | ``` | 62 | ``` |
| @@ -75,6 +76,36 @@ python3 <skill-dir>/scripts/knowledge_query.py --knowledge-root <repo> search \ | |||
| 75 | - `text` 对 title、description、tags/aliases 和正文加权;`literal` 要求完整查询串出现在这些字段中,并按精确标题、标题片段、摘要、正文的顺序降权。普通正文链接与其他正文一起进入索引,不按专用关系块标记排除内容。 | 76 | - `text` 对 title、description、tags/aliases 和正文加权;`literal` 要求完整查询串出现在这些字段中,并按精确标题、标题片段、摘要、正文的顺序降权。普通正文链接与其他正文一起进入索引,不按专用关系块标记排除内容。 |
| 76 | - 默认 status 为 `stable`,limit 必须大于零。 | 77 | - 默认 status 为 `stable`,limit 必须大于零。 |
| 77 | 78 | ||
| 79 | +### 显式场景 Scope | ||
| 80 | + | ||
| 81 | +`--scope-file` 读取 JSON/YAML 文件,`--scope-json` 接收同样结构的对象文本;两者互斥。输入是 Scope 对象本身,不再包一层 `scope`。显式提交表示调用方已核对条件;脚本仅做 Registry 别名归一化,不从自然语言推断执行模式、模型特征或数值阈值。 | ||
| 82 | + | ||
| 83 | +Scope 对卡片和查询均可选,可以只提交有依据的部分条件,不要求每个维度齐全。先按领域选择 Registry 的 namespace、适用范围与匹配类型;Query 共用 Contract 内的处理器,不在此解释领域特例。目前生产 Registry 仅有 `model.inference.v1`,下例的维度不适用于其他领域。新领域或新的匹配语义按[机器契约](../../../../governance/specs/schemas.md)扩展,不在查询文件中指定脚本或可执行判断。 | ||
| 84 | + | ||
| 85 | +```yaml | ||
| 86 | +schema: model.inference.v1 | ||
| 87 | +any_of: | ||
| 88 | + - all_of: | ||
| 89 | + phase: [decode] | ||
| 90 | + objective: [tpot] | ||
| 91 | + model_features: | ||
| 92 | + all_of: [moe] | ||
| 93 | + none_of: [mla] | ||
| 94 | + parallelism: | ||
| 95 | + all_of: [tp] | ||
| 96 | + none_of: [cp] | ||
| 97 | +``` | ||
| 98 | + | ||
| 99 | +上述查询表示已确认具备 MoE、不具备 MLA、启用 TP 且未启用 CP。`model_features.none_of` 仍只用于查询,不能写入卡片;`parallelism.none_of` 可用于有来源依据的卡片及显式确认的查询,不能把未提及的特征或并行方式自动补成否定。只声明 TP 不证明 CP 已关闭;未启用 CP 也不代表单卡。正反集合可同时声明,但至少一项非空且不得相交。 | ||
| 100 | + | ||
| 101 | +查询明确启用卡片禁止的并行方式,或明确关闭卡片必需的并行方式时为 `conflict`;缺少启用/关闭证据时为 `unknown`。未声明的并行否定、其他维度的否定权限不会自动扩展。`objective` 参与精确匹配,`latency` 不自动包含 `ttft` 或 `tpot`;有来源支持时,卡片显式列出多个目标。维度使用 Registry 的 `scope_schemas`,模型推理范围见 [Frontmatter](../../../../governance/specs/frontmatter.md)。卡片使用规范值,只有显式查询接受受控别名。 | ||
| 102 | + | ||
| 103 | +未提交 Scope 时保留原查询范围、评分、排序和请求结构。提交后,先在原过滤与文本候选内排除 `conflict`,保留 `match`、`unknown`,再取前 k;评分不加 Scope 权重,同分时匹配项优先,其余仍按原路径顺序。无 Scope 的旧卡片或其他已注册 namespace 的相关卡片为未知,未注册 schema 或非法条件报错,现有平台等前置条件保持不变。 | ||
| 104 | + | ||
| 105 | +结果新增 `scope_match`,包含三态 `state`、逐对的查询/卡片场景分支、枚举交集和缺失/冲突原因;文本输出将未知项标为“适用性待确认”。顶层 `scope_filter` 回显归一化条件、启用状态、冲突过滤计数和被过滤项原因。候选条件匹配不表示全文各分支均适用,正文与证据仍需核对。 | ||
| 106 | + | ||
| 107 | +Scope 条件绑定到请求指纹和 receipt,重放使用同一契约;旧 receipt 未带 Scope 时保持原语义。查询不会回写卡片或自动准备索引;Schema、Registry、Contract 变更后仍由维护者先准备匹配版本的索引。 | ||
| 108 | + | ||
| 78 | ### Receipt | 109 | ### Receipt |
| 79 | 110 | ||
| 80 | `--receipt` 原子写入 schema、完整请求、canonical 请求指纹、知识仓状态、索引版本、语料/治理指纹、命中卡片指纹和原始结果。目标目录项必须不存在;已有文件、目录和符号链接(包括悬空链接)均拒绝,不沿目标链接创建其他文件。这一约定同时适用于 search、state 和 outcome receipt。审计流程的 `--decision` 使用稳定 ID,并把文件放在调用方实验目录。State 校验会重新计算请求指纹,要求每条 receipt 的平台集合与 state 目标完全相同,核对完整当前索引 provenance,并按原请求重放过滤、评分、排序和命中;ready receipt 绑定每个 search receipt 的文件 SHA-256,outcome 校验会再次确认这些文件未变化。 | 111 | `--receipt` 原子写入 schema、完整请求、canonical 请求指纹、知识仓状态、索引版本、语料/治理指纹、命中卡片指纹和原始结果。目标目录项必须不存在;已有文件、目录和符号链接(包括悬空链接)均拒绝,不沿目标链接创建其他文件。这一约定同时适用于 search、state 和 outcome receipt。审计流程的 `--decision` 使用稳定 ID,并把文件放在调用方实验目录。State 校验会重新计算请求指纹,要求每条 receipt 的平台集合与 state 目标完全相同,核对完整当前索引 provenance,并按原请求重放过滤、评分、排序和命中;ready receipt 绑定每个 search receipt 的文件 SHA-256,outcome 校验会再次确认这些文件未变化。 |
| @@ -8,6 +8,58 @@ | |||
| 8 | 8 | ||
| 9 | ## 审计查询 | 9 | ## 审计查询 |
| 10 | 10 | ||
| 11 | +### 自然语言到查询 Scope | ||
| 12 | + | ||
| 13 | +Agent 负责提取,脚本不承担自然语言推断。先保留用户原意,再按下列规则组织检索: | ||
| 14 | + | ||
| 15 | +1. 记录平台与每个候选维度的值、原文或已读配置证据,以及状态 `explicit`、`inferred`、`unknown`。置信度只描述推断把握,不能替代确认;复杂任务在审计说明中记录,轻量查询无需另建文件。这些说明不写进 Scope 对象或卡片 Frontmatter。 | ||
| 16 | +2. 用户直接给出的条件、用户已确认的提取结果,以及任务明确指定配置中的确定条件,可提交为 Scope;无需反复让用户确认同一句明确要求。模型名联想、优化意图和未定义数值阈值仍是推断或未知,只作文本线索。 | ||
| 17 | +3. 先按任务领域选择 Registry 中已注册且适用于目标路径的 Schema,再将已确认部分按该 Schema 的维度和规范值构造 Scope,不将模型推理字段套到其他领域。遗漏未知维度,不补 `any`,不把未提及特征写为 `none_of`。`any` 必须有明确不限制该维度的依据。只有推断线索时不传 Scope,保持原平台、状态、Profile 和文本检索。 | ||
| 18 | +4. 同一场景不同维度放进同一个 `all_of`;确实独立的备选场景分成 `any_of` 分支。不能把“Prefill+GE 或 Decode+eager”展开成允许四种组合;长上下文、batch、并发、服务模式、并行和部署拓扑分别表达。定量数值不自行映射为大/小或长/短。 | ||
| 19 | +5. 先宽召回;若缺失条件会改变适用结论,优先询问那个条件,并继续不依赖答案的阅读。结果说明实际用于过滤的已确认条件、只作线索的推断,以及命中分支的待确认前提。`unknown` 结果仍可相关,不能包装成“确认适用”。 | ||
| 20 | + | ||
| 21 | +| 用户表达或证据 | Scope 处理 | 阅读与提示 | | ||
| 22 | +| --- | --- | --- | | ||
| 23 | +| “A3 上优化 Prefill 吞吐” | 平台 A3;`phase=[prefill]`、`objective=[throughput]` | 执行模式、模型结构未知,不从吞吐推断大 batch | | ||
| 24 | +| “这个模型可能是 MoE,想降低时延” | 只提交明确的 `objective=[latency]`;MoE 作文本线索 | 确认结构前不提交 `model_features`;不自动改成 TPOT/TTFT | | ||
| 25 | +| “长上下文、小 batch、online” | 三个独立维度;不推导并发或 PD | 并发和部署拓扑按需要再核对 | | ||
| 26 | +| “模型具有 MoE 和 MLA” | `model_features.all_of=[moe,mla]` | 卡片只要求 MoE 时可满足该特征条件;其他前提仍逐项检查 | | ||
| 27 | +| “未提 MLA” | 不写 `none_of` | 只有明确不具备 MLA 才能提交查询否定条件 | | ||
| 28 | +| “已启用 TP,明确未启用 CP” | `parallelism.all_of=[tp]`、`parallelism.none_of=[cp]` | 未启用 CP 不等于单卡;其他并行状态不推断 | | ||
| 29 | +| “已启用 TP”,未说明 CP | 只写 `parallelism.all_of=[tp]` | 卡片要求未启用 CP 时仍为未知,不以未提及作为否定证据 | | ||
| 30 | +| “Prefill+GE 或 Decode+eager” | 两个 `any_of` 分支,每个分支分别 `all_of` | 按命中分支读正文,禁止跨分支补齐证据 | | ||
| 31 | +| 来源说明 FP8/MXFP4 按模块共同组成混合方案 | 现有量化枚举的 OR 不能表达该组合;卡片暂不写 `quantization` | 具体混合配置按正文核对;单一量化格式查询可召回该卡,但量化适用性仍未知 | | ||
| 32 | + | ||
| 33 | +以上是 Agent 消费流程,尚不表示存在自动提取器或推断准确率实验。Scope 的结构、归一化与过滤仍只使用共享 Contract。 | ||
| 34 | + | ||
| 35 | +### 从相关结果到结论 | ||
| 36 | + | ||
| 37 | +检索返回不等于问题已回答。先区分机制解释、方案选择、停止启用和实际部署:机制解释应覆盖题目所需的结构与限制,不能只解释一个实现分支便宣称整个主题已经完成;方案选择还需满足会改变选择的前提。缺失前提可保留条件化结论,不以追问更多环境信息代替已能完成的解释。 | ||
| 38 | + | ||
| 39 | +- 优先读取规范入口,再按需沿正文链接补齐相关规则或实现。前五项都只有局部线索时,用主题名称或关键约束补搜;不能从某个故障卡直接拼出整个机制或通用配置。 | ||
| 40 | +- 区分设计目标、固定源码实际编排和已观测效果。来源写“重叠”时仍需核对分支、依赖与同步,不能从指南示意图宣称实现已在副流并行,或从局部修复推断后续状态传播已验证。 | ||
| 41 | +- 按目标版本核对后端接口、守卫和配置格式;同名技术的历史注册、同步或量化路径不能合并为当前通用步骤。未读的关联链接只作补证入口。 | ||
| 42 | +- 输出分别说明已回答的结论、仍缺的条件和未执行的验证。条件不足的回答可以有价值,但不把它计为已完成方案选择或已完成部署;指标分母为零时不虚报正确率。 | ||
| 43 | + | ||
| 44 | +### 未知原因与补证顺序 | ||
| 45 | + | ||
| 46 | +先判断候选是否能回答当前 decision,再判断适用条件。只共享“图模式”“并行”或“量化”等词、却讨论另一种机制或故障的卡片,不进入答案证据;不必为填满前五项采用它们。这个选卡步骤不改变原始搜索结果、排序或 receipt。 | ||
| 47 | + | ||
| 48 | +主题不相关不能直接判为 Scope `conflict`:冲突需要已知适用前提之间有明确矛盾。模型特征、实现或环境未说明时仍可能是 `unknown`;不能从未提及某个集合特征推导“不具备”。 | ||
| 49 | + | ||
| 50 | +对相关但适用性未知的卡片,说明具体缺口及下一步,不只重复“环境待确认”: | ||
| 51 | + | ||
| 52 | +| 缺口 | 下一步 | 完成边界 | | ||
| 53 | +| --- | --- | --- | | ||
| 54 | +| 查询缺少会改变方案选择的条件 | 先读正文找出实际前提,再询问最关键的少量条件;继续可独立完成的机制解释 | 答复前保持条件化结论,不从模型名、部署惯例或优化目标补硬 Scope | | ||
| 55 | +| 固定来源暂不可读或未准备 | 保留来源 URL、revision 和未读范围;需要恢复时交给维护者或资源准备流程 | Query/Lint 不自动下载;卡片引用来源不等于当前已复核该来源 | | ||
| 56 | +| 来源本身没有平台、版本、效果或测量记录 | 区分已有机制证据与缺失的适用性、效果证据,记录所需材料 | 不把缺字段改成 `any`、`agnostic` 或虚构 `verified`;不能用补问用户环境代替来源证据 | | ||
| 57 | +| 顶层 Scope 无法表达正文的否定或组合条件 | 逐项核对具体实现与正文,明确尚未由机器判定的前提 | 不把缺失集合成员当作“不具备”,不把 OR 当成联合方案;机器 `match` 只覆盖已声明条件 | | ||
| 58 | + | ||
| 59 | +将一个复合问题拆成多个 decision 补搜时,各自保留查询和证据。沿链接或补搜取得的证据可以帮助完成解释,但不得回填为原始 Top5 命中;评测中仍保留原题、原分母和原始失败。 | ||
| 60 | + | ||
| 61 | +### 审计步骤 | ||
| 62 | + | ||
| 11 | 1. 建立目标边界:实体、platform、CANN version、工程形态、dtype/layout、workload 和验证边界。 | 63 | 1. 建立目标边界:实体、platform、CANN version、工程形态、dtype/layout、workload 和验证边界。 |
| 12 | 2. 列出 decisions;每个 decision 只回答一个判断,例如参数单位、错误信号是否有已验证修复、性能瓶颈的主导机制。 | 64 | 2. 列出 decisions;每个 decision 只回答一个判断,例如参数单位、错误信号是否有已验证修复、性能瓶颈的主导机制。 |
| 13 | 3. 为每个 decision 运行窄查询,保存独立 receipt。互不依赖的 decision 可以并行,但 receipt 路径不能复用。 | 65 | 3. 为每个 decision 运行窄查询,保存独立 receipt。互不依赖的 decision 可以并行,但 receipt 路径不能复用。 |
| @@ -21,6 +21,7 @@ from retrieval.index import ( # noqa: E402 | |||
| 21 | index_tokenizer, | 21 | index_tokenizer, |
| 22 | literal_scores, | 22 | literal_scores, |
| 23 | load_cards, | 23 | load_cards, |
| 24 | + load_contract, | ||
| 24 | open_index, | 25 | open_index, |
| 25 | phrase_matches, | 26 | phrase_matches, |
| 26 | posting_scores, | 27 | posting_scores, |
| @@ -280,6 +281,8 @@ def request_payload(args: argparse.Namespace, mapped_task: str | None) -> dict[s | |||
| 280 | "target_technology": args.target_technology, | 281 | "target_technology": args.target_technology, |
| 281 | "status": args.status, | 282 | "status": args.status, |
| 282 | } | 283 | } |
| 284 | + if getattr(args, "scope", None) is not None: | ||
| 285 | + filters["scope"] = args.scope | ||
| 283 | return { | 286 | return { |
| 284 | "query": args.query, | 287 | "query": args.query, |
| 285 | "decision": args.decision, | 288 | "decision": args.decision, |
| @@ -307,6 +310,10 @@ def search_args_from_request(request: dict[str, Any]) -> argparse.Namespace: | |||
| 307 | for field in FILTER_FIELDS: | 310 | for field in FILTER_FIELDS: |
| 308 | if request.get(field) != filters.get(field): | 311 | if request.get(field) != filters.get(field): |
| 309 | raise ValueError(f"receipt request.{field} must equal request.filters.{field}") | 312 | raise ValueError(f"receipt request.{field} must equal request.filters.{field}") |
| 313 | + if ("scope" in request or "scope" in filters) and ( | ||
| 314 | + "scope" not in request or "scope" not in filters or request["scope"] != filters["scope"] | ||
| 315 | + ): | ||
| 316 | + raise ValueError("receipt request.scope must equal request.filters.scope") | ||
| 310 | 317 | ||
| 311 | query = request.get("query") | 318 | query = request.get("query") |
| 312 | if not isinstance(query, str) or not query.strip(): | 319 | if not isinstance(query, str) or not query.strip(): |
| @@ -365,22 +372,34 @@ def search_args_from_request(request: dict[str, Any]) -> argparse.Namespace: | |||
| 365 | json=True, | 372 | json=True, |
| 366 | text=False, | 373 | text=False, |
| 367 | receipt=None, | 374 | receipt=None, |
| 375 | + scope=request.get("scope"), | ||
| 376 | + scope_json=None, | ||
| 377 | + scope_file=None, | ||
| 368 | ) | 378 | ) |
| 369 | 379 | ||
| 370 | 380 | ||
| 371 | -def search_payload( | 381 | +def search_candidates( |
| 372 | - root: Path, | 382 | + root: Path, cards: list[IndexedCard], args: argparse.Namespace, |
| 373 | - cards: list[IndexedCard], | 383 | +) -> tuple[dict[int, IndexedCard], str | None, Any]: |
| 374 | - index_metadata: dict[str, Any], | 384 | + """Share declared eligibility and Scope parsing without doing text ranking.""" |
| 375 | - connection, | ||
| 376 | - args: argparse.Namespace, | ||
| 377 | - *, | ||
| 378 | - include_current_repository: bool, | ||
| 379 | -) -> dict[str, Any]: | ||
| 380 | registries, profiles = load_governance(root) | 385 | registries, profiles = load_governance(root) |
| 381 | if args.card_type and args.card_type not in profiles: | 386 | if args.card_type and args.card_type not in profiles: |
| 382 | raise ValueError(f"unknown knowledge type: {args.card_type}") | 387 | raise ValueError(f"unknown knowledge type: {args.card_type}") |
| 383 | validate_filters(registries, args) | 388 | validate_filters(registries, args) |
| 389 | + scope_registry = None | ||
| 390 | + raw_scope = getattr(args, "scope", None) | ||
| 391 | + scope_text = getattr(args, "scope_json", None) | ||
| 392 | + scope_file = getattr(args, "scope_file", None) | ||
| 393 | + if raw_scope is not None or scope_text is not None or scope_file is not None: | ||
| 394 | + contract, _ = load_contract(root) | ||
| 395 | + scope_registry = contract.scope_registry | ||
| 396 | + if scope_file is not None: | ||
| 397 | + scope_text = Path(scope_file).read_text(encoding="utf-8") | ||
| 398 | + args.scope = ( | ||
| 399 | + contract.parse_scope(scope_text, query=True) | ||
| 400 | + if scope_text is not None | ||
| 401 | + else scope_registry.normalize(raw_scope, query=True) | ||
| 402 | + ) | ||
| 384 | mapped_task = task_tag(registries, args.task) | 403 | mapped_task = task_tag(registries, args.task) |
| 385 | selected_route = None | 404 | selected_route = None |
| 386 | if args.technology: | 405 | if args.technology: |
| @@ -396,6 +415,19 @@ def search_payload( | |||
| 396 | for card in cards | 415 | for card in cards |
| 397 | if matches(card, args, mapped_task, selected_route_domains) | 416 | if matches(card, args, mapped_task, selected_route_domains) |
| 398 | } | 417 | } |
| 418 | + return eligible, mapped_task, scope_registry | ||
| 419 | + | ||
| 420 | + | ||
| 421 | +def search_payload( | ||
| 422 | + root: Path, | ||
| 423 | + cards: list[IndexedCard], | ||
| 424 | + index_metadata: dict[str, Any], | ||
| 425 | + connection, | ||
| 426 | + args: argparse.Namespace, | ||
| 427 | + *, | ||
| 428 | + include_current_repository: bool, | ||
| 429 | +) -> dict[str, Any]: | ||
| 430 | + eligible, mapped_task, scope_registry = search_candidates(root, cards, args) | ||
| 399 | normalized_query = args.query.lower().strip() | 431 | normalized_query = args.query.lower().strip() |
| 400 | if not normalized_query: | 432 | if not normalized_query: |
| 401 | raise ValueError("--query must not be empty") | 433 | raise ValueError("--query must not be empty") |
| @@ -413,17 +445,29 @@ def search_payload( | |||
| 413 | raw_scores[doc_id] = raw_scores.get(doc_id, 0.0) + 4.0 | 445 | raw_scores[doc_id] = raw_scores.get(doc_id, 0.0) + 4.0 |
| 414 | 446 | ||
| 415 | ranked: list[tuple[float, IndexedCard]] = [] | 447 | ranked: list[tuple[float, IndexedCard]] = [] |
| 448 | + scope_matches: dict[int, dict[str, Any]] = {} | ||
| 449 | + scope_conflicts = [] | ||
| 416 | for doc_id, score in raw_scores.items(): | 450 | for doc_id, score in raw_scores.items(): |
| 417 | card = eligible.get(doc_id) | 451 | card = eligible.get(doc_id) |
| 418 | if card is None or score <= 0: | 452 | if card is None or score <= 0: |
| 419 | continue | 453 | continue |
| 454 | + if scope_registry is not None: | ||
| 455 | + applicability = scope_registry.compare(args.scope, card.metadata.get("scope")) | ||
| 456 | + scope_matches[doc_id] = applicability | ||
| 457 | + if applicability["state"] == "conflict": | ||
| 458 | + scope_conflicts.append({"path": card.path, "scope_match": applicability}) | ||
| 459 | + continue | ||
| 420 | tier = trust_tier(card.metadata) | 460 | tier = trust_tier(card.metadata) |
| 421 | if tier == "unverified": | 461 | if tier == "unverified": |
| 422 | score *= 0.8 | 462 | score *= 0.8 |
| 423 | elif tier == "machine-confirmed": | 463 | elif tier == "machine-confirmed": |
| 424 | score *= 0.9 | 464 | score *= 0.9 |
| 425 | ranked.append((score, card)) | 465 | ranked.append((score, card)) |
| 426 | - ranked.sort(key=lambda item: (-item[0], item[1].path)) | 466 | + ranked.sort(key=lambda item: ( |
| 467 | + -item[0], | ||
| 468 | + 0 if scope_registry is None or scope_matches[item[1].doc_id]["state"] == "match" else 1, | ||
| 469 | + item[1].path, | ||
| 470 | + )) | ||
| 427 | ranked = ranked[: args.limit] | 471 | ranked = ranked[: args.limit] |
| 428 | 472 | ||
| 429 | results = [] | 473 | results = [] |
| @@ -458,9 +502,12 @@ def search_payload( | |||
| 458 | "source_technology", | 502 | "source_technology", |
| 459 | "target_technology", | 503 | "target_technology", |
| 460 | "replaced_by", | 504 | "replaced_by", |
| 505 | + "scope", | ||
| 461 | ): | 506 | ): |
| 462 | if field in metadata: | 507 | if field in metadata: |
| 463 | result[field] = metadata[field] | 508 | result[field] = metadata[field] |
| 509 | + if scope_registry is not None: | ||
| 510 | + result["scope_match"] = scope_matches[card.doc_id] | ||
| 464 | results.append(result) | 511 | results.append(result) |
| 465 | 512 | ||
| 466 | request = request_payload(args, mapped_task) | 513 | request = request_payload(args, mapped_task) |
| @@ -483,6 +530,13 @@ def search_payload( | |||
| 483 | "results": results, | 530 | "results": results, |
| 484 | "hits": results, | 531 | "hits": results, |
| 485 | } | 532 | } |
| 533 | + if scope_registry is not None: | ||
| 534 | + payload["scope_filter"] = { | ||
| 535 | + "enabled": True, | ||
| 536 | + "query_scope": args.scope, | ||
| 537 | + "excluded_count": len(scope_conflicts), | ||
| 538 | + "excluded": sorted(scope_conflicts, key=lambda item: item["path"]), | ||
| 539 | + } | ||
| 486 | return payload | 540 | return payload |
| 487 | 541 | ||
| 488 | 542 | ||
| @@ -523,6 +577,10 @@ def search( | |||
| 523 | print(f" path: {hit['path']}") | 577 | print(f" path: {hit['path']}") |
| 524 | print(f" local_path: {hit['local_path']} ({hit['card_lines']} lines)") | 578 | print(f" local_path: {hit['local_path']} ({hit['card_lines']} lines)") |
| 525 | print(f" description: {hit['description']}") | 579 | print(f" description: {hit['description']}") |
| 580 | + if "scope_match" in hit: | ||
| 581 | + print(f" scope_match: {hit['scope_match']['state']}") | ||
| 582 | + if hit["scope_match"]["state"] == "unknown": | ||
| 583 | + print(" 适用性待确认: " + json.dumps(hit["scope_match"], ensure_ascii=False)) | ||
| 526 | if not results: | 584 | if not results: |
| 527 | print("no matches") | 585 | print("no matches") |
| 528 | else: | 586 | else: |
| @@ -556,6 +614,9 @@ def build_parser() -> argparse.ArgumentParser: | |||
| 556 | search_cmd.add_argument("--include-shared", action="store_true") | 614 | search_cmd.add_argument("--include-shared", action="store_true") |
| 557 | search_cmd.add_argument("--source-technology") | 615 | search_cmd.add_argument("--source-technology") |
| 558 | search_cmd.add_argument("--target-technology") | 616 | search_cmd.add_argument("--target-technology") |
| 617 | + scope_input = search_cmd.add_mutually_exclusive_group() | ||
| 618 | + scope_input.add_argument("--scope-json", help="Explicit confirmed Scope as JSON/YAML; unknown applicability is retained") | ||
| 619 | + scope_input.add_argument("--scope-file", help="Read explicit confirmed query Scope from a JSON/YAML file") | ||
| 559 | search_cmd.add_argument( | 620 | search_cmd.add_argument( |
| 560 | "--status", | 621 | "--status", |
| 561 | choices=["stable", "draft", "deprecated", "all"], | 622 | choices=["stable", "draft", "deprecated", "all"], |
| @@ -41,7 +41,9 @@ | |||
| 41 | 41 | ||
| 42 | Schema、Profile、Registry、Contract、Spec、Skill 或测试若互相矛盾,不要自行选择其中一份绕过。先定位唯一事实源,再在同一变更中修正执行逻辑、文档和回归。 | 42 | Schema、Profile、Registry、Contract、Spec、Skill 或测试若互相矛盾,不要自行选择其中一份绕过。先定位唯一事实源,再在同一变更中修正执行逻辑、文档和回归。 |
| 43 | 43 | ||
| 44 | -场景适用条件 `scope` 是可选字段,只填写来源支持且有过滤价值的部分条件,不要求每张卡填写,不以填写率验收;未知不补成 `any`,也不写入查询专用的否定条件。各领域独立在 Registry 的 `scope_schemas` 中定义 namespace、适用范围、维度和匹配语义,与路径 route 的 `scopes` 分区区分。Schema、Profile 和同版本 Contract 共同校验,匹配扩展只能使用受审查的 Contract 处理器;消费端是否启用场景过滤以其实际公开接口为准。 | 44 | +场景适用条件 `scope` 的词表使用 Registry 的 `scope_schemas`,与路径 route 的 `scopes` 分区区分。Scope 的校验和三态匹配共用 Contract;无显式查询 Scope 时保持原查询行为,有显式已确认条件时仅排除明确冲突并保留未知项。卡片只写来源支持的条件,不以缺字段或检索相关推导普遍适用,也不把查询专用的否定条件写进卡片。 |
| 45 | + | ||
| 46 | +`scope` 保持可选,不要求每张卡填写,不以填写率作为质量或迁移完成标准。有来源依据且有过滤价值的部分条件即可声明,不要求补齐全部维度,也不为凑齐条件填写 `any`。不同领域独立定义 Schema、适用范围、维度及匹配语义,不套用模型推理词表;共享对象外壳和三态结果协议,特殊匹配通过同版本 Contract 内受审查的处理器扩展,不在 Query、Lint 或单张卡中增加局部解释。 | ||
| 45 | 47 | ||
| 46 | ## 修改知识卡 | 48 | ## 修改知识卡 |
| 47 | 49 | ||
| @@ -58,6 +60,7 @@ Schema、Profile、Registry、Contract、Spec、Skill 或测试若互相矛盾 | |||
| 58 | 60 | ||
| 59 | - 文件名使用小写 `snake_case`,自由主题的集合目录使用复数;Registry 控制的 Operator 源码分类保持其注册 ID。Registry 中注册的所有 domain 根目录必须保留 `index.md`;只有 `ops` 和 `model` 预建注册 route,其他领域的下一层目录随首张真实知识创建。 | 61 | - 文件名使用小写 `snake_case`,自由主题的集合目录使用复数;Registry 控制的 Operator 源码分类保持其注册 ID。Registry 中注册的所有 domain 根目录必须保留 `index.md`;只有 `ops` 和 `model` 预建注册 route,其他领域的下一层目录随首张真实知识创建。 |
| 60 | - Operator 和 Runbook 只能使用 Registry 中声明的附加层级;`concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可增加小写 `snake_case` 主题层级,其他路径 Profile 默认直接放卡片。 | 62 | - Operator 和 Runbook 只能使用 Registry 中声明的附加层级;`concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可增加小写 `snake_case` 主题层级,其他路径 Profile 默认直接放卡片。 |
| 63 | +- 附加层级还须遵守 Registry `paths.forbidden_sections` 的 domain/route/Profile 约束;模型推理 Guide 与 Optimization 不再创建 `experience` 层,其他领域不自动继承此限制。 | ||
| 61 | - `index.md` 只存在于 `knowledge/`。每个目录都必须有一个索引,完整且无重复地列出直接子目录和直接知识卡,并为每项提供摘要;domain 根和 `ops`/`model` 预建 route 可只有 index,且不表示已有知识覆盖。 | 64 | - `index.md` 只存在于 `knowledge/`。每个目录都必须有一个索引,完整且无重复地列出直接子目录和直接知识卡,并为每项提供摘要;domain 根和 `ops`/`model` 预建 route 可只有 index,且不表示已有知识覆盖。 |
| 62 | - `index.md` 供人阅读:本次新增或修改的一级标题、导航项名称和摘要应以中文表述,API 名、芯片型号、代码符号和报错原文等必要技术标识保留原文。知识卡的 `title`、`description` 会成为索引新增条目的默认文案,新增或修改卡片时应写成中文可读的标题和摘要;确需保留英文原题时,在索引的显示名称和摘要中补足中文说明。 | 65 | - `index.md` 供人阅读:本次新增或修改的一级标题、导航项名称和摘要应以中文表述,API 名、芯片型号、代码符号和报错原文等必要技术标识保留原文。知识卡的 `title`、`description` 会成为索引新增条目的默认文案,新增或修改卡片时应写成中文可读的标题和摘要;确需保留英文原题时,在索引的显示名称和摘要中补足中文说明。 |
| 63 | - 只有 `knowledge/index.md` 声明 `okf_version: "0.2"`,其他索引不带 Frontmatter,所有索引均不参与知识检索。 | 66 | - 只有 `knowledge/index.md` 声明 `okf_version: "0.2"`,其他索引不带 Frontmatter,所有索引均不参与知识检索。 |
| @@ -157,6 +157,8 @@ Git 来源固定到提交;普通文档来源优先使用稳定版本页,Regi | |||
| 157 | 157 | ||
| 158 | 额外字段由路径 Profile 决定。`type` 必须写入,但其值由 Profile 的 `okf_type` 唯一决定;不要在卡片中自行创造类型,也不要重复填写可从路径推导的 domain、technology/scope 或 Concept ID。 | 158 | 额外字段由路径 Profile 决定。`type` 必须写入,但其值由 Profile 的 `okf_type` 唯一决定;不要在卡片中自行创造类型,也不要重复填写可从路径推导的 domain、technology/scope 或 Concept ID。 |
| 159 | 159 | ||
| 160 | +模型推理知识先区分机制、优化决策、任务流程和故障诊断,再按[路径规范](governance/specs/paths.md)选择入口。同一规则只维护一份正文,案例保留具体实现及证据,其他视图通过链接引用。Guide 与 Optimization 不再使用 `experience` 层。可选 `scope` 只填写来源支持的场景条件;未知字段留空缺失,不以 `any` 代替未知,也不因移动卡片或通过 Lint 补造 `verified`。 | ||
| 161 | + | ||
| 160 | 具体字段语义、生命周期、平台表示、OKF `verified` 事件、信任层级、标签和各 Profile 允许字段见 [Frontmatter 与证据](governance/specs/frontmatter.md)。 | 162 | 具体字段语义、生命周期、平台表示、OKF `verified` 事件、信任层级、标签和各 Profile 允许字段见 [Frontmatter 与证据](governance/specs/frontmatter.md)。 |
| 161 | 163 | ||
| 162 | ### 3.7 编写正文 | 164 | ### 3.7 编写正文 |
| @@ -308,6 +310,8 @@ A/B test 指旧方案与候选方案在同一任务集上的对照实验,可 | |||
| 308 | 310 | ||
| 309 | 可复用评测用例和驱动放在 `evals/`;本地可重建报告放在 ignored 的 `artifacts/`。RFC/PR 中保留结果摘要,并附上评审者可访问的完整报告或复现材料,不能只提供作者机器上的绝对路径。Skill 内部命令仍由对应 `SKILL.md` 或 `references/` 维护,RFC/PR 引用这些说明并记录本次实际执行配置。 | 311 | 可复用评测用例和驱动放在 `evals/`;本地可重建报告放在 ignored 的 `artifacts/`。RFC/PR 中保留结果摘要,并附上评审者可访问的完整报告或复现材料,不能只提供作者机器上的绝对路径。Skill 内部命令仍由对应 `SKILL.md` 或 `references/` 维护,RFC/PR 引用这些说明并记录本次实际执行配置。 |
| 310 | 312 | ||
| 313 | +Scope A/B 的人工标注可用 [评审输入校验器](evals/scope_review.py) 核对:评审记录绑定冻结用例、迁移等价映射与两组检索快照,并完整覆盖实际返回项。运行方式见 `python3 -m evals.scope_review --help`;缺少人工标注时输出待评审状态,不生成精度结果。该工具只计算检索指标,不能代替工作流评审、仓库门禁或 committer 检视。 | ||
| 314 | + | ||
| 311 | ### 5.4 实现、验证并提交 PR | 315 | ### 5.4 实现、验证并提交 PR |
| 312 | 316 | ||
| 313 | 当前维护 `knowledge-ingest`、`knowledge-query`、`knowledge-lint` 三项基础 Skill、复用 Query 的只读专用消费流程 `ascendc-api-knowledge-query`、知识仓内的 `ops-knowledge-trace-ingest`(trace → runbooks 生产),以及 `ops-knowledge-vv-ingest`、`ops-knowledge-cv-ingest` 两项源码级 Operator producer。VV 用于全部可达 Device 节点均不含 Cube/AIC 或 MIX_CV 的算子,CV 用于至少存在一个可达 Cube/AIC 或 MIX_CV 节点的算子;两者都由任务决定入口,并按现有 call-mode 落库。仅 Host/Tiling 内部入口目前没有对应分类,须先报告缺口,不能套用 ACLNN 或 Direct Invoke 路径;完整边界见 [Knowledge Skills](governance/specs/skills.md)。新增其他 Skill 需要先证明现有职责无法合理承载,并完成架构和发布评审。 | 317 | 当前维护 `knowledge-ingest`、`knowledge-query`、`knowledge-lint` 三项基础 Skill、复用 Query 的只读专用消费流程 `ascendc-api-knowledge-query`、知识仓内的 `ops-knowledge-trace-ingest`(trace → runbooks 生产),以及 `ops-knowledge-vv-ingest`、`ops-knowledge-cv-ingest` 两项源码级 Operator producer。VV 用于全部可达 Device 节点均不含 Cube/AIC 或 MIX_CV 的算子,CV 用于至少存在一个可达 Cube/AIC 或 MIX_CV 节点的算子;两者都由任务决定入口,并按现有 call-mode 落库。仅 Host/Tiling 内部入口目前没有对应分类,须先报告缺口,不能套用 ACLNN 或 Direct Invoke 路径;完整边界见 [Knowledge Skills](governance/specs/skills.md)。新增其他 Skill 需要先证明现有职责无法合理承载,并完成架构和发布评审。 |
| @@ -24,6 +24,7 @@ python3 .agents/skills/knowledge-query/scripts/knowledge_index.py \ | |||
| 24 | --knowledge-root . verify --level full | 24 | --knowledge-root . verify --level full |
| 25 | # Gate recall/ranking by aggregate targets; query and filter contract errors still fail. | 25 | # Gate recall/ranking by aggregate targets; query and filter contract errors still fail. |
| 26 | python3 evals/run_retrieval.py --quality-gate | 26 | python3 evals/run_retrieval.py --quality-gate |
| 27 | +python3 evals/run_retrieval.py --cases evals/model_inference_scope_cases.yaml | ||
| 27 | python3 -m unittest discover -s evals -p 'test_*.py' | 28 | python3 -m unittest discover -s evals -p 'test_*.py' |
| 28 | python3 -m unittest discover -s .agents/skills/ops-knowledge-trace-ingest/scripts -p 'test_*.py' | 29 | python3 -m unittest discover -s .agents/skills/ops-knowledge-trace-ingest/scripts -p 'test_*.py' |
| 29 | python3 .agents/skills/ops-knowledge-trace-ingest/scripts/field_notes_lint.py | 30 | python3 .agents/skills/ops-knowledge-trace-ingest/scripts/field_notes_lint.py |
| @@ -9,11 +9,20 @@ | |||
| 9 | # See LICENSE in the root of the software repository for the full text of the License. | 9 | # See LICENSE in the root of the software repository for the full text of the License. |
| 10 | # ---------------------------------------------------------------------------------------------------------- | 10 | # ---------------------------------------------------------------------------------------------------------- |
| 11 | 11 | ||
| 12 | -"""Run retrieval cases against knowledge-query.""" | 12 | +"""Run retrieval cases against knowledge-query. |
| 13 | + | ||
| 14 | +Cases may supply a structured ``scope`` and assert returned machine states via | ||
| 15 | +``expected_scope_states`` or audited conflicts via ``expected_scope_excluded``. | ||
| 16 | +These assertions verify the retrieval contract, not human applicability labels. | ||
| 17 | +The original case file and quality thresholds remain the default. | ||
| 18 | +Optional preflight checks expected-card eligibility before running or scoring | ||
| 19 | +queries. It checks declared filters, not semantic correctness or text ranking. | ||
| 20 | +""" | ||
| 13 | 21 | ||
| 14 | from __future__ import annotations | 22 | from __future__ import annotations |
| 15 | 23 | ||
| 16 | import argparse | 24 | import argparse |
| 25 | +import importlib.util | ||
| 17 | import json | 26 | import json |
| 18 | import math | 27 | import math |
| 19 | import subprocess | 28 | import subprocess |
| @@ -29,6 +38,112 @@ QUERY = ROOT / ".agents/skills/knowledge-query/scripts/knowledge_query.py" | |||
| 29 | CASES = ROOT / "evals/retrieval_cases.yaml" | 38 | CASES = ROOT / "evals/retrieval_cases.yaml" |
| 30 | 39 | ||
| 31 | 40 | ||
| 41 | +def query_arguments(case: dict) -> list[str]: | ||
| 42 | + """Use identical query arguments for preflight and the actual execution.""" | ||
| 43 | + arguments = ["search", "--query", str(case["query"]), "--json"] | ||
| 44 | + filters = case.get("filters", {}) | ||
| 45 | + if not isinstance(filters, dict) or any(not isinstance(key, str) for key in filters): | ||
| 46 | + raise ValueError("case.filters must be an object with string keys") | ||
| 47 | + # Generic flags normalize underscores to hyphens; argparse may also accept | ||
| 48 | + # unique prefixes. Keep every Scope spelling on the audited case.scope path. | ||
| 49 | + if any(key.replace("_", "-").startswith("scope") for key in filters): | ||
| 50 | + raise ValueError("use case.scope as the only Scope input") | ||
| 51 | + if "scope" in case: | ||
| 52 | + if not isinstance(case["scope"], dict): | ||
| 53 | + raise ValueError("case.scope must be a Scope object") | ||
| 54 | + arguments.extend(["--scope-json", json.dumps(case["scope"], ensure_ascii=False)]) | ||
| 55 | + for key, value in filters.items(): | ||
| 56 | + flag = "--type" if key == "type" else f"--{key.replace('_', '-')}" | ||
| 57 | + if isinstance(value, bool): | ||
| 58 | + if value: | ||
| 59 | + arguments.append(flag) | ||
| 60 | + continue | ||
| 61 | + for item in value if isinstance(value, list) else [value]: | ||
| 62 | + arguments.extend([flag, str(item)]) | ||
| 63 | + return arguments | ||
| 64 | + | ||
| 65 | + | ||
| 66 | +def preflight_cases(data: dict, roots: dict[str, Path]) -> dict: | ||
| 67 | + """Reject impossible positive expectations without changing recall denominators. | ||
| 68 | + | ||
| 69 | + Unknown Scope remains eligible. Alternative groups need at least one eligible | ||
| 70 | + member. Negative and expected-error cases are left to the actual query run. | ||
| 71 | + Only the prepared index and the Query Skill's own matching code are used. | ||
| 72 | + """ | ||
| 73 | + cases = data.get("cases") | ||
| 74 | + if not isinstance(cases, list): | ||
| 75 | + raise ValueError("retrieval cases must be a list") | ||
| 76 | + spec = importlib.util.spec_from_file_location("retrieval_preflight_query", QUERY) | ||
| 77 | + module = importlib.util.module_from_spec(spec) | ||
| 78 | + spec.loader.exec_module(module) | ||
| 79 | + opened, reports, ids = {}, [], set() | ||
| 80 | + try: | ||
| 81 | + for case in cases: | ||
| 82 | + if not isinstance(case, dict) or not isinstance(case.get("id"), str) or not case["id"]: | ||
| 83 | + raise ValueError("preflight cases require nonempty string ids") | ||
| 84 | + if case["id"] in ids: | ||
| 85 | + raise ValueError("duplicate preflight case id") | ||
| 86 | + ids.add(case["id"]) | ||
| 87 | + expected, expected_any = case.get("expected", []), case.get("expected_any", []) | ||
| 88 | + if not isinstance(expected, list) or not isinstance(expected_any, list): | ||
| 89 | + raise ValueError("expected and expected_any must be lists") | ||
| 90 | + groups = [[path] for path in expected] + expected_any | ||
| 91 | + if any(not isinstance(g, list) or not g or any(not isinstance(p, str) or not p for p in g) for g in groups): | ||
| 92 | + raise ValueError("expected groups must be nonempty lists of paths") | ||
| 93 | + if case.get("expected_error") or not groups: | ||
| 94 | + reports.append({"id": case["id"], "status": "not_positive_case", "groups": []}) | ||
| 95 | + continue | ||
| 96 | + corpus = str(case.get("corpus", "repository")) | ||
| 97 | + if corpus not in roots: | ||
| 98 | + raise ValueError(f"unknown retrieval corpus: {corpus}") | ||
| 99 | + root = roots[corpus] | ||
| 100 | + if corpus not in opened: | ||
| 101 | + connection, metadata = module.open_index(root) | ||
| 102 | + opened[corpus] = (connection, module.load_cards(connection), metadata) | ||
| 103 | + _, cards, _ = opened[corpus] | ||
| 104 | + args = module.build_parser().parse_args(query_arguments(case)) | ||
| 105 | + module.apply_default_platform(args) | ||
| 106 | + eligible, mapped_task, scope_registry = module.search_candidates(root, cards, args) | ||
| 107 | + by_path = {card.path: card for card in cards} | ||
| 108 | + checked = [] | ||
| 109 | + for group in groups: | ||
| 110 | + alternatives = [] | ||
| 111 | + for path in group: | ||
| 112 | + card = by_path.get(path) | ||
| 113 | + comparison = None | ||
| 114 | + if card is None: | ||
| 115 | + reason = "missing_concept" | ||
| 116 | + elif card.doc_id not in eligible: | ||
| 117 | + reason = "metadata_filter_conflict" | ||
| 118 | + else: | ||
| 119 | + if scope_registry is not None: | ||
| 120 | + comparison = scope_registry.compare(args.scope, card.metadata.get("scope")) | ||
| 121 | + reason = "scope_conflict" if comparison and comparison["state"] == "conflict" else None | ||
| 122 | + alternatives.append({"path": path, "eligible": reason is None, "reason": reason, | ||
| 123 | + "scope_match": comparison, | ||
| 124 | + "metadata": None if card is None else { | ||
| 125 | + "domain": card.domain, "technology": card.technology, | ||
| 126 | + "profile": card.profile, "status": card.metadata.get("status"), | ||
| 127 | + "platforms": card.metadata.get("platforms"), | ||
| 128 | + }}) | ||
| 129 | + checked.append({"eligible": any(a["eligible"] for a in alternatives), "alternatives": alternatives}) | ||
| 130 | + reports.append({"id": case["id"], "status": "eligible" if all(g["eligible"] for g in checked) else "invalid_expectation", | ||
| 131 | + "request": module.request_payload(args, mapped_task), "groups": checked}) | ||
| 132 | + failures = [r["id"] for r in reports if r["status"] == "invalid_expectation"] | ||
| 133 | + return {"status": "invalid_expectations" if failures else "ready", "failed_cases": failures, | ||
| 134 | + "cases": reports, "indexes": {name: value[2] for name, value in opened.items()}, | ||
| 135 | + "note": "Declared eligibility only; not relevance, applicability confirmation, or human review. No cases were removed from scoring."} | ||
| 136 | + finally: | ||
| 137 | + for connection, _, _ in opened.values(): | ||
| 138 | + connection.close() | ||
| 139 | + | ||
| 140 | + | ||
| 141 | +def scope_state(item: object) -> str | None: | ||
| 142 | + value = item.get("scope_match") if isinstance(item, dict) else None | ||
| 143 | + state = value.get("state") if isinstance(value, dict) else None | ||
| 144 | + return state if isinstance(state, str) else None | ||
| 145 | + | ||
| 146 | + | ||
| 32 | def run_cases( | 147 | def run_cases( |
| 33 | data: dict[str, object], roots: dict[str, Path], *, quality_gate: bool = False | 148 | data: dict[str, object], roots: dict[str, Path], *, quality_gate: bool = False |
| 34 | ) -> int: | 149 | ) -> int: |
| @@ -75,25 +190,23 @@ def run_cases( | |||
| 75 | ) | 190 | ) |
| 76 | expected = {path for group in groups for path in group} | 191 | expected = {path for group in groups for path in group} |
| 77 | expected_total += len(groups) | 192 | expected_total += len(groups) |
| 78 | - command = [ | 193 | + command = [sys.executable, str(QUERY), "--knowledge-root", str(roots[corpus])] |
| 79 | - sys.executable, | 194 | + scoped = "scope" in case |
| 80 | - str(QUERY), | 195 | + scope_states = case.get("expected_scope_states", {}) |
| 81 | - "--knowledge-root", | 196 | + scope_excluded = case.get("expected_scope_excluded", []) |
| 82 | - str(roots[corpus]), | 197 | + if not isinstance(scope_states, dict) or any( |
| 83 | - "search", | 198 | + not isinstance(path, str) or not path |
| 84 | - "--query", | 199 | + or not isinstance(state, str) or state not in {"match", "unknown"} |
| 85 | - str(case["query"]), | 200 | + for path, state in scope_states.items() |
| 86 | - "--json", | 201 | + ): |
| 87 | - ] | 202 | + raise ValueError("expected_scope_states must map Concept IDs to match/unknown") |
| 88 | - for key, value in case.get("filters", {}).items(): | 203 | + if not isinstance(scope_excluded, list) or any( |
| 89 | - flag = "--type" if key == "type" else f"--{key.replace('_', '-')}" | 204 | + not isinstance(path, str) or not path for path in scope_excluded |
| 90 | - if isinstance(value, bool): | 205 | + ): |
| 91 | - if value: | 206 | + raise ValueError("expected_scope_excluded must be a list of Concept IDs") |
| 92 | - command.append(flag) | 207 | + if (scope_states or scope_excluded) and not scoped: |
| 93 | - continue | 208 | + raise ValueError("Scope assertions require an explicit case.scope") |
| 94 | - values = value if isinstance(value, list) else [value] | 209 | + command.extend(query_arguments(case)) |
| 95 | - for item in values: | ||
| 96 | - command.extend([flag, str(item)]) | ||
| 97 | started = time.perf_counter() | 210 | started = time.perf_counter() |
| 98 | completed = subprocess.run(command, check=False, text=True, capture_output=True) | 211 | completed = subprocess.run(command, check=False, text=True, capture_output=True) |
| 99 | latencies_ms.append((time.perf_counter() - started) * 1000) | 212 | latencies_ms.append((time.perf_counter() - started) * 1000) |
| @@ -119,6 +232,31 @@ def run_cases( | |||
| 119 | ) | 232 | ) |
| 120 | hit_items = {item["path"]: item for item in payload["hits"]} | 233 | hit_items = {item["path"]: item for item in payload["hits"]} |
| 121 | hits = set(hit_items) | 234 | hits = set(hit_items) |
| 235 | + scope_errors = [] | ||
| 236 | + if scoped: | ||
| 237 | + scope_filter = payload.get("scope_filter") | ||
| 238 | + if not isinstance(scope_filter, dict) or scope_filter.get("enabled") is not True: | ||
| 239 | + scope_errors.append("explicit Scope filter was not enabled") | ||
| 240 | + scope_filter = {} | ||
| 241 | + for path, item in hit_items.items(): | ||
| 242 | + if scope_state(item) not in {"match", "unknown"}: | ||
| 243 | + scope_errors.append(f"{path}: invalid returned Scope state") | ||
| 244 | + for path, state in scope_states.items(): | ||
| 245 | + actual = scope_state(hit_items.get(path)) | ||
| 246 | + if actual != state: | ||
| 247 | + scope_errors.append(f"{path}: expected {state}, got {actual}") | ||
| 248 | + exclusions = scope_filter.get("excluded", []) | ||
| 249 | + if not isinstance(exclusions, list) or any( | ||
| 250 | + not isinstance(item, dict) or not isinstance(item.get("path"), str) | ||
| 251 | + or scope_state(item) != "conflict" for item in exclusions | ||
| 252 | + ): | ||
| 253 | + scope_errors.append("invalid Scope exclusion audit") | ||
| 254 | + exclusions = [] | ||
| 255 | + excluded_items = {item["path"]: item for item in exclusions} | ||
| 256 | + for path in scope_excluded: | ||
| 257 | + actual = scope_state(excluded_items.get(path)) | ||
| 258 | + if path in hits or actual != "conflict": | ||
| 259 | + scope_errors.append(f"{path}: expected audited conflict exclusion") | ||
| 122 | missing = [group for group in groups if not hits.intersection(group)] | 260 | missing = [group for group in groups if not hits.intersection(group)] |
| 123 | leaked = set(case.get("excluded", [])) & hits | 261 | leaked = set(case.get("excluded", [])) & hits |
| 124 | expected_found += len(groups) - len(missing) | 262 | expected_found += len(groups) - len(missing) |
| @@ -164,18 +302,19 @@ def run_cases( | |||
| 164 | and not set(platforms) <= set(supported) | 302 | and not set(platforms) <= set(supported) |
| 165 | ): | 303 | ): |
| 166 | out_of_scope.append(f"{path}: platform") | 304 | out_of_scope.append(f"{path}: platform") |
| 167 | - if missing or leaked or missing_fields or count_mismatch or out_of_scope: | 305 | + if missing or leaked or missing_fields or count_mismatch or out_of_scope or scope_errors: |
| 168 | # Only recall misses may be covered by aggregate quality thresholds. | 306 | # Only recall misses may be covered by aggregate quality thresholds. |
| 169 | # All command, count, field and filtering contracts remain mandatory. | 307 | # All command, count, field and filtering contracts remain mandatory. |
| 170 | reports = ( | 308 | reports = ( |
| 171 | misses | 309 | misses |
| 172 | - if quality_gate and not (leaked or missing_fields or count_mismatch or out_of_scope) | 310 | + if quality_gate and not (leaked or missing_fields or count_mismatch or out_of_scope or scope_errors) |
| 173 | else failures | 311 | else failures |
| 174 | ) | 312 | ) |
| 175 | reports.append( | 313 | reports.append( |
| 176 | f"{case['id']}: missing={sorted(missing)} leaked={sorted(leaked)} " | 314 | f"{case['id']}: missing={sorted(missing)} leaked={sorted(leaked)} " |
| 177 | f"missing_fields={missing_fields} count={payload['count']} " | 315 | f"missing_fields={missing_fields} count={payload['count']} " |
| 178 | f"expected_count={expected_count} out_of_scope={out_of_scope}" | 316 | f"expected_count={expected_count} out_of_scope={out_of_scope}" |
| 317 | + + (f" scope_errors={scope_errors}" if scope_errors else "") | ||
| 179 | ) | 318 | ) |
| 180 | else: | 319 | else: |
| 181 | print(f"PASS {case['id']}") | 320 | print(f"PASS {case['id']}") |
| @@ -209,16 +348,29 @@ def run_cases( | |||
| 209 | 348 | ||
| 210 | def main() -> int: | 349 | def main() -> int: |
| 211 | parser = argparse.ArgumentParser(description=__doc__) | 350 | parser = argparse.ArgumentParser(description=__doc__) |
| 351 | + parser.add_argument("--cases", type=Path, default=CASES, help="Optional retrieval case file; defaults to the existing corpus.") | ||
| 212 | parser.add_argument( | 352 | parser.add_argument( |
| 213 | "--quality-gate", | 353 | "--quality-gate", |
| 214 | action="store_true", | 354 | action="store_true", |
| 215 | help="Require recall/MRR above thresholds; other assertions still fail.", | 355 | help="Require recall/MRR above thresholds; other assertions still fail.", |
| 216 | ) | 356 | ) |
| 357 | + preflight = parser.add_mutually_exclusive_group() | ||
| 358 | + preflight.add_argument("--preflight", action="store_true", help="Reject impossible positive expectations before running queries; keep unknown Scope eligible.") | ||
| 359 | + preflight.add_argument("--preflight-only", action="store_true", help="Only report expected-card eligibility from the prepared index; no ranking metrics.") | ||
| 217 | args = parser.parse_args() | 360 | args = parser.parse_args() |
| 218 | try: | 361 | try: |
| 219 | - data = yaml.safe_load(CASES.read_text(encoding="utf-8")) | 362 | + data = yaml.safe_load(args.cases.read_text(encoding="utf-8")) |
| 220 | if not isinstance(data, dict): | 363 | if not isinstance(data, dict): |
| 221 | raise ValueError("retrieval case file must contain an object") | 364 | raise ValueError("retrieval case file must contain an object") |
| 365 | + if args.preflight_only and args.quality_gate: | ||
| 366 | + raise ValueError("--preflight-only does not run the quality gate") | ||
| 367 | + if args.preflight or args.preflight_only: | ||
| 368 | + report = preflight_cases(data, {"repository": ROOT}) | ||
| 369 | + print(json.dumps(report, ensure_ascii=False, indent=2)) | ||
| 370 | + if report["status"] != "ready": | ||
| 371 | + return 2 | ||
| 372 | + if args.preflight_only: | ||
| 373 | + return 0 | ||
| 222 | return run_cases(data, {"repository": ROOT}, quality_gate=args.quality_gate) | 374 | return run_cases(data, {"repository": ROOT}, quality_gate=args.quality_gate) |
| 223 | except (OSError, ValueError, yaml.YAMLError) as exc: | 375 | except (OSError, ValueError, yaml.YAMLError) as exc: |
| 224 | print(f"FAIL retrieval harness: {exc}", file=sys.stderr) | 376 | print(f"FAIL retrieval harness: {exc}", file=sys.stderr) |
| @@ -0,0 +1,119 @@ | |||
| 1 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +"""Aggregate frozen, separately reviewed Scope judgments without inventing labels.""" | ||
| 12 | + | ||
| 13 | +from __future__ import annotations | ||
| 14 | + | ||
| 15 | +from typing import Any | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +def summarize_workflows( | ||
| 19 | + decisions: list[dict[str, Any]], | ||
| 20 | + created_cards: list[dict[str, Any]], | ||
| 21 | + stable_cards: list[dict[str, Any]], | ||
| 22 | +) -> dict[str, Any]: | ||
| 23 | + """Aggregate separate reviewer judgments for RFC workflow metrics. | ||
| 24 | + | ||
| 25 | + These inputs are human review records, not machine Scope states or lint | ||
| 26 | + results. Pending labels fail instead of silently improving the denominator. | ||
| 27 | + This validates shape only; it cannot authenticate the reviewer. | ||
| 28 | + human_scope_complete judges whether applicability is adequately documented, | ||
| 29 | + including in prose; it does not require optional Scope metadata or every | ||
| 30 | + dimension to be populated, and must not be inferred from field coverage. | ||
| 31 | + """ | ||
| 32 | + def records(values, required): | ||
| 33 | + if not isinstance(values, list): | ||
| 34 | + raise ValueError("workflow judgments must be lists") | ||
| 35 | + ids = set() | ||
| 36 | + for item in values: | ||
| 37 | + if not isinstance(item, dict) or not isinstance(item.get("id"), str) or not item["id"]: | ||
| 38 | + raise ValueError("workflow judgment requires an id") | ||
| 39 | + if item["id"] in ids: | ||
| 40 | + raise ValueError("duplicate workflow judgment id") | ||
| 41 | + ids.add(item["id"]) | ||
| 42 | + if any(not isinstance(item.get(key), bool) for key in required): | ||
| 43 | + raise ValueError("workflow metrics require complete separate human judgments") | ||
| 44 | + return values | ||
| 45 | + | ||
| 46 | + decisions = records(decisions, ["completed"]) | ||
| 47 | + completed = [item for item in decisions if item["completed"]] | ||
| 48 | + if any(not isinstance(item.get("human_applicable"), bool) for item in completed): | ||
| 49 | + raise ValueError("completed decisions require human_applicable") | ||
| 50 | + created_cards = records(created_cards, ["human_duplicate"]) | ||
| 51 | + evidence_fields = ["human_source_complete", "human_scope_complete", "human_validation_complete", "human_boundary_complete"] | ||
| 52 | + stable_cards = records(stable_cards, evidence_fields) | ||
| 53 | + return { | ||
| 54 | + "decision_accuracy": sum(item["human_applicable"] for item in completed) / len(completed) if completed else None, | ||
| 55 | + "completed_decisions": len(completed), | ||
| 56 | + "incomplete_decisions": len(decisions) - len(completed), | ||
| 57 | + "duplicate_rate": sum(item["human_duplicate"] for item in created_cards) / len(created_cards) if created_cards else None, | ||
| 58 | + "reviewed_created_cards": len(created_cards), | ||
| 59 | + "evidence_completeness": sum(all(item[key] for key in evidence_fields) for item in stable_cards) / len(stable_cards) if stable_cards else None, | ||
| 60 | + "reviewed_stable_cards": len(stable_cards), | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + | ||
| 64 | +def summarize(cases: list[dict[str, Any]]) -> dict[str, Any]: | ||
| 65 | + """Each case supplies confirmed_scope, expected paths and ordered judgments. | ||
| 66 | + | ||
| 67 | + Results must carry a separate human_judgment, not Query's scope_match. This | ||
| 68 | + validates the evaluation input shape; it cannot authenticate human review. | ||
| 69 | + All cases contribute recall/coverage; only explicit Scope cases contribute | ||
| 70 | + micro-averaged applicability ratios. Zero denominators are represented by None. | ||
| 71 | + """ | ||
| 72 | + if not isinstance(cases, list): | ||
| 73 | + raise ValueError("metric cases must be a list") | ||
| 74 | + counts = {"match": 0, "conflict": 0, "unknown": 0} | ||
| 75 | + recall_values = [] | ||
| 76 | + positive = nonempty = explicit = 0 | ||
| 77 | + returned_counts = [] | ||
| 78 | + for case in cases: | ||
| 79 | + if not isinstance(case, dict) or not isinstance(case.get("confirmed_scope"), bool): | ||
| 80 | + raise ValueError("metric cases require a confirmed_scope boolean") | ||
| 81 | + expected = case.get("expected") | ||
| 82 | + results = case.get("results") | ||
| 83 | + if not isinstance(expected, list) or any(not isinstance(path, str) or not path for path in expected): | ||
| 84 | + raise ValueError("expected must be a list of Concept IDs") | ||
| 85 | + if len(set(expected)) != len(expected): | ||
| 86 | + raise ValueError("expected contains duplicate Concept IDs") | ||
| 87 | + if not isinstance(results, list): | ||
| 88 | + raise ValueError("results must be an ordered list") | ||
| 89 | + top = results[:5] | ||
| 90 | + paths = [] | ||
| 91 | + for item in top: | ||
| 92 | + if not isinstance(item, dict) or not isinstance(item.get("path"), str) or not item["path"]: | ||
| 93 | + raise ValueError("result requires a Concept ID path") | ||
| 94 | + paths.append(item["path"]) | ||
| 95 | + if case["confirmed_scope"]: | ||
| 96 | + judgment = item.get("human_judgment") | ||
| 97 | + if not isinstance(judgment, str) or judgment not in counts: | ||
| 98 | + raise ValueError("explicit Scope metrics require separate human_judgment labels") | ||
| 99 | + counts[judgment] += 1 | ||
| 100 | + if len(set(paths)) != len(paths): | ||
| 101 | + raise ValueError("top five results contain duplicate Concept IDs") | ||
| 102 | + returned_counts.append(len(top)) | ||
| 103 | + if expected: | ||
| 104 | + positive += 1 | ||
| 105 | + nonempty += bool(top) | ||
| 106 | + recall_values.append(len(set(paths) & set(expected)) / len(expected)) | ||
| 107 | + explicit += case["confirmed_scope"] | ||
| 108 | + total = sum(counts.values()) | ||
| 109 | + return { | ||
| 110 | + "scope_cases": explicit, | ||
| 111 | + "scope_returned": total, | ||
| 112 | + "scope_precision_at_5": counts["match"] / total if total else None, | ||
| 113 | + "conflict_rate_at_5": counts["conflict"] / total if total else None, | ||
| 114 | + "unknown_rate_at_5": counts["unknown"] / total if total else None, | ||
| 115 | + "recall_at_5": sum(recall_values) / len(recall_values) if recall_values else None, | ||
| 116 | + "nonempty_recall_rate": nonempty / positive if positive else None, | ||
| 117 | + "returned_counts": returned_counts, | ||
| 118 | + "counts": counts, | ||
| 119 | + } | ||
| @@ -0,0 +1,205 @@ | |||
| 1 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +"""Validate frozen A/B review inputs before computing RFC Scope metrics. | ||
| 12 | + | ||
| 13 | +Run as ``python -m evals.scope_review --help``. No judgments are inferred from | ||
| 14 | +machine matches. Reviewer identity is a declaration, not authenticated here. | ||
| 15 | +""" | ||
| 16 | + | ||
| 17 | +from __future__ import annotations | ||
| 18 | + | ||
| 19 | +import argparse | ||
| 20 | +import copy | ||
| 21 | +import hashlib | ||
| 22 | +import json | ||
| 23 | +from pathlib import Path | ||
| 24 | + | ||
| 25 | +from evals.scope_metrics import summarize | ||
| 26 | + | ||
| 27 | + | ||
| 28 | +def equivalence_fingerprint(equivalence: dict) -> str: | ||
| 29 | + raw = json.dumps(equivalence, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode() | ||
| 30 | + return hashlib.sha256(raw).hexdigest() | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +def evaluate_review(review: dict, captures: dict, capture_sha256: dict, equivalence: dict) -> dict: | ||
| 34 | + """Reject changed captures and incomplete coverage; pending labels do not score.""" | ||
| 35 | + if not isinstance(review, dict) or review.get("schema_version") != "rfc47.human-evaluation.v1": | ||
| 36 | + raise ValueError("unsupported human review schema") | ||
| 37 | + if not isinstance(review.get("human_review_complete"), bool): | ||
| 38 | + raise ValueError("human_review_complete must be a boolean") | ||
| 39 | + if not isinstance(equivalence, dict) or any( | ||
| 40 | + not isinstance(key, str) or not key or not isinstance(value, str) or not value | ||
| 41 | + for key, value in equivalence.items() | ||
| 42 | + ): | ||
| 43 | + raise ValueError("equivalence must map Concept IDs to Concept IDs") | ||
| 44 | + | ||
| 45 | + def canonical(path): | ||
| 46 | + seen = set() | ||
| 47 | + while path in equivalence and equivalence[path] != path: | ||
| 48 | + if path in seen: | ||
| 49 | + raise ValueError("equivalence contains a cycle") | ||
| 50 | + seen.add(path) | ||
| 51 | + path = equivalence[path] | ||
| 52 | + return path | ||
| 53 | + | ||
| 54 | + for path in equivalence: | ||
| 55 | + canonical(path) | ||
| 56 | + if review.get("equivalence_sha256") != equivalence_fingerprint(equivalence): | ||
| 57 | + raise ValueError("review migration equivalence has changed") | ||
| 58 | + if review.get("capture_sha256") != capture_sha256 or set(capture_sha256) != {"A", "B"}: | ||
| 59 | + raise ValueError("review is not bound to these exact capture bytes") | ||
| 60 | + variants = review.get("variants") | ||
| 61 | + if not isinstance(variants, dict) or set(variants) != {"A", "B"} or set(captures) != {"A", "B"}: | ||
| 62 | + raise ValueError("both A and B are required") | ||
| 63 | + pending = [] | ||
| 64 | + reviewer = review.get("reviewer") | ||
| 65 | + if reviewer is None: | ||
| 66 | + pending.append("reviewer") | ||
| 67 | + elif not isinstance(reviewer, str) or not reviewer.strip(): | ||
| 68 | + raise ValueError("reviewer must be a nonempty name") | ||
| 69 | + ids = None | ||
| 70 | + ordered = {} | ||
| 71 | + for variant in ["A", "B"]: | ||
| 72 | + capture = captures[variant] | ||
| 73 | + if not isinstance(capture, dict) or capture.get("variant") != variant: | ||
| 74 | + raise ValueError("capture variant mismatch") | ||
| 75 | + cases, judgments = capture.get("cases"), variants[variant] | ||
| 76 | + if not isinstance(cases, list) or not cases or not isinstance(judgments, list): | ||
| 77 | + raise ValueError("captures and judgments require nonempty case lists") | ||
| 78 | + case_ids = [item.get("id") for item in cases if isinstance(item, dict)] | ||
| 79 | + if len(case_ids) != len(cases) or any(not isinstance(key, str) or not key for key in case_ids): | ||
| 80 | + raise ValueError("capture case requires an id") | ||
| 81 | + if len(set(case_ids)) != len(case_ids): | ||
| 82 | + raise ValueError("capture contains duplicate case ids") | ||
| 83 | + if ids is not None and case_ids != ids: | ||
| 84 | + raise ValueError("A/B case coverage or order differs") | ||
| 85 | + ids = case_ids | ||
| 86 | + if [item.get("id") for item in judgments if isinstance(item, dict)] != ids or len(judgments) != len(cases): | ||
| 87 | + raise ValueError("review case coverage or order differs from capture") | ||
| 88 | + ordered[variant] = judgments | ||
| 89 | + for capture_case, case in zip(cases, judgments): | ||
| 90 | + prefix = f"{variant}.{case['id']}" | ||
| 91 | + if "error" in capture_case: | ||
| 92 | + raise ValueError("failed capture cannot be scored as a successful experiment") | ||
| 93 | + results, labels = capture_case.get("results"), case.get("results") | ||
| 94 | + if not isinstance(results, list) or len(results) > 5 or not isinstance(labels, list): | ||
| 95 | + raise ValueError("capture and judgments require ordered top-five results") | ||
| 96 | + paths = [item.get("path") for item in results if isinstance(item, dict)] | ||
| 97 | + if len(paths) != len(results) or any(not isinstance(path, str) or not path for path in paths): | ||
| 98 | + raise ValueError("capture result requires a Concept ID") | ||
| 99 | + if len(set(paths)) != len(paths): | ||
| 100 | + raise ValueError("duplicate capture result") | ||
| 101 | + if [item.get("path") for item in labels if isinstance(item, dict)] != paths or len(labels) != len(results): | ||
| 102 | + raise ValueError("review result paths or order differ from capture") | ||
| 103 | + for rank, item in enumerate(labels, 1): | ||
| 104 | + label = item.get("human_judgment") | ||
| 105 | + if label is None: | ||
| 106 | + pending.append(f"{prefix}.results.{rank}") | ||
| 107 | + elif not isinstance(label, str) or label not in {"match", "conflict", "unknown"}: | ||
| 108 | + raise ValueError("invalid human_judgment") | ||
| 109 | + value = case.get("confirmed_scope") | ||
| 110 | + if value is None: | ||
| 111 | + pending.append(f"{prefix}.confirmed_scope") | ||
| 112 | + elif not isinstance(value, bool): | ||
| 113 | + raise ValueError("confirmed_scope must be boolean after review") | ||
| 114 | + for key in ["expected", "excluded", "answer_points"]: | ||
| 115 | + values = case.get(key) | ||
| 116 | + if values is None: | ||
| 117 | + pending.append(f"{prefix}.{key}") | ||
| 118 | + elif not isinstance(values, list) or any(not isinstance(item, str) or not item for item in values): | ||
| 119 | + raise ValueError(f"{key} must be a list of nonempty strings") | ||
| 120 | + elif len(values) != len(set(values)): | ||
| 121 | + raise ValueError(f"duplicate {key}") | ||
| 122 | + if captures["A"].get("cases_input_sha256") != captures["B"].get("cases_input_sha256"): | ||
| 123 | + raise ValueError("A/B captures used different frozen inputs") | ||
| 124 | + frozen = captures["A"].get("cases_input_sha256") | ||
| 125 | + if not isinstance(frozen, str) or len(frozen) != 64 or any(ch not in "0123456789abcdef" for ch in frozen): | ||
| 126 | + raise ValueError("capture requires a frozen cases SHA-256") | ||
| 127 | + if review.get("cases_input_sha256") != frozen: | ||
| 128 | + raise ValueError("review is bound to different frozen cases") | ||
| 129 | + for a, b, ca, cb in zip(ordered["A"], ordered["B"], captures["A"]["cases"], captures["B"]["cases"]): | ||
| 130 | + requests = [] | ||
| 131 | + for captured in [ca, cb]: | ||
| 132 | + request = copy.deepcopy(captured.get("request")) | ||
| 133 | + if not isinstance(request, dict) or not isinstance(request.get("filters"), dict): | ||
| 134 | + raise ValueError("capture requires a query request and filters") | ||
| 135 | + request.pop("scope", None) | ||
| 136 | + request["filters"].pop("scope", None) | ||
| 137 | + requests.append(request) | ||
| 138 | + if requests[0] != requests[1]: | ||
| 139 | + raise ValueError("A/B differ in query conditions other than the Scope extension") | ||
| 140 | + if a.get("confirmed_scope") != b.get("confirmed_scope"): | ||
| 141 | + raise ValueError("A/B must review applicability against the same confirmed conditions") | ||
| 142 | + for key in ["expected", "excluded"]: | ||
| 143 | + if a.get(key) is not None and b.get(key) is not None: | ||
| 144 | + if {canonical(path) for path in a[key]} != {canonical(path) for path in b[key]}: | ||
| 145 | + raise ValueError(f"A/B {key} differs after migration equivalence") | ||
| 146 | + if a.get("answer_points") is not None and b.get("answer_points") is not None and a["answer_points"] != b["answer_points"]: | ||
| 147 | + raise ValueError("A/B answer points must use the same frozen gold") | ||
| 148 | + if pending or not review["human_review_complete"]: | ||
| 149 | + if review["human_review_complete"]: | ||
| 150 | + raise ValueError("review marked complete with pending judgments") | ||
| 151 | + return {"status": "awaiting_human_review", "cases": len(ids), "pending_fields": len(pending), | ||
| 152 | + "pending": pending, "metrics": None} | ||
| 153 | + | ||
| 154 | + metrics = {} | ||
| 155 | + for variant, cases in ordered.items(): | ||
| 156 | + metrics[variant] = summarize(cases) | ||
| 157 | + recalls = [] | ||
| 158 | + violations = [] | ||
| 159 | + for case in cases: | ||
| 160 | + expected = {canonical(path) for path in case["expected"]} | ||
| 161 | + returned = {canonical(item["path"]) for item in case["results"]} | ||
| 162 | + if expected: | ||
| 163 | + recalls.append(len(expected & returned) / len(expected)) | ||
| 164 | + excluded = {canonical(path) for path in case["excluded"]} | ||
| 165 | + if returned & excluded: | ||
| 166 | + violations.append({"id": case["id"], "excluded_hits": sorted(returned & excluded)}) | ||
| 167 | + metrics[variant]["recall_at_5"] = sum(recalls) / len(recalls) if recalls else None | ||
| 168 | + metrics[variant]["gold_exclusion_violations"] = violations | ||
| 169 | + a, b = metrics["A"], metrics["B"] | ||
| 170 | + directions = {"recall_at_5": "nondecreasing", "nonempty_recall_rate": "nondecreasing", | ||
| 171 | + "scope_precision_at_5": "increasing", "conflict_rate_at_5": "nonincreasing"} | ||
| 172 | + criteria = {} | ||
| 173 | + for key, direction in directions.items(): | ||
| 174 | + av, bv = a[key], b[key] | ||
| 175 | + criteria[key] = av is not None and bv is not None and ( | ||
| 176 | + bv > av if direction == "increasing" else bv >= av if direction == "nondecreasing" else bv <= av) | ||
| 177 | + criteria["gold_exclusions"] = not b["gold_exclusion_violations"] | ||
| 178 | + return {"status": "reviewed", "cases": len(ids), "metrics": metrics, "criteria": criteria, | ||
| 179 | + "retrieval_criteria_passed": all(criteria.values()), | ||
| 180 | + "acceptance_complete": False, | ||
| 181 | + "note": "Retrieval metrics only; workflow judgments, compatibility and repository gates are separate."} | ||
| 182 | + | ||
| 183 | + | ||
| 184 | +def main(argv=None): | ||
| 185 | + parser = argparse.ArgumentParser(description=__doc__) | ||
| 186 | + parser.add_argument("--review", type=Path, required=True) | ||
| 187 | + parser.add_argument("--baseline", type=Path, required=True) | ||
| 188 | + parser.add_argument("--candidate", type=Path, required=True) | ||
| 189 | + parser.add_argument("--equivalence", type=Path, required=True) | ||
| 190 | + args = parser.parse_args(argv) | ||
| 191 | + try: | ||
| 192 | + raw = {key: path.read_bytes() for key, path in [("A", args.baseline), ("B", args.candidate)]} | ||
| 193 | + report = evaluate_review(json.loads(args.review.read_text()), | ||
| 194 | + {key: json.loads(value) for key, value in raw.items()}, | ||
| 195 | + {key: hashlib.sha256(value).hexdigest() for key, value in raw.items()}, | ||
| 196 | + json.loads(args.equivalence.read_text())) | ||
| 197 | + except (OSError, ValueError, TypeError) as exc: | ||
| 198 | + print(json.dumps({"status": "invalid", "error": str(exc)}, ensure_ascii=False)) | ||
| 199 | + return 2 | ||
| 200 | + print(json.dumps(report, ensure_ascii=False, indent=2)) | ||
| 201 | + return 2 if report["status"] != "reviewed" else 0 if report["retrieval_criteria_passed"] else 1 | ||
| 202 | + | ||
| 203 | + | ||
| 204 | +if __name__ == "__main__": | ||
| 205 | + raise SystemExit(main()) | ||
| @@ -162,13 +162,21 @@ class DocumentationContractTests(unittest.TestCase): | |||
| 162 | self.assertIn(document.relative_to(self.root).as_posix(), readme) | 162 | self.assertIn(document.relative_to(self.root).as_posix(), readme) |
| 163 | 163 | ||
| 164 | def test_reviewed_model_python_examples_have_valid_syntax(self) -> None: | 164 | def test_reviewed_model_python_examples_have_valid_syntax(self) -> None: |
| 165 | - for relative in ( | 165 | + required_examples = { |
| 166 | - "knowledge/model/inference/concepts/models/sana_video.md", | 166 | + "knowledge/model/inference/optimizations/rules/dit_cnn_operator_algebraic_rewrite_npu.md", |
| 167 | + "knowledge/model/inference/guides/models/sana_video_deployment.md", | ||
| 167 | "knowledge/model/inference/concepts/parallelism/ulysses.md", | 168 | "knowledge/model/inference/concepts/parallelism/ulysses.md", |
| 168 | - ): | 169 | + } |
| 170 | + reviewed = required_examples | { | ||
| 171 | + path.relative_to(self.root).as_posix() | ||
| 172 | + for path in (self.root / "knowledge/model/inference/concepts").rglob("*.md") | ||
| 173 | + if path.name != "index.md" and path.parent.name != "models" | ||
| 174 | + } | ||
| 175 | + for relative in sorted(reviewed): | ||
| 169 | text = (self.root / relative).read_text(encoding="utf-8") | 176 | text = (self.root / relative).read_text(encoding="utf-8") |
| 170 | blocks = list(re.finditer(r"(?ms)^```python\n(.*?)^```", text)) | 177 | blocks = list(re.finditer(r"(?ms)^```python\n(.*?)^```", text)) |
| 171 | - self.assertTrue(blocks, relative) | 178 | + if relative in required_examples: |
| 179 | + self.assertTrue(blocks, relative) | ||
| 172 | for block in blocks: | 180 | for block in blocks: |
| 173 | line = text[:block.start(1)].count("\n") + 1 | 181 | line = text[:block.start(1)].count("\n") + 1 |
| 174 | with self.subTest(card=relative, line=line): | 182 | with self.subTest(card=relative, line=line): |
| @@ -0,0 +1,163 @@ | |||
| 1 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +from __future__ import annotations | ||
| 12 | + | ||
| 13 | +import hashlib | ||
| 14 | +import io | ||
| 15 | +from pathlib import Path | ||
| 16 | +import tempfile | ||
| 17 | +import unittest | ||
| 18 | +from contextlib import redirect_stderr | ||
| 19 | +from unittest.mock import patch | ||
| 20 | + | ||
| 21 | +import yaml | ||
| 22 | + | ||
| 23 | +from evals.run_retrieval import main, preflight_cases, query_arguments, run_cases | ||
| 24 | +from evals.test_retrieval_index import make_root, run_index | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +def scope(**conditions): | ||
| 28 | + return {"schema": "model.inference.v1", "any_of": [{"all_of": conditions}]} | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +class RetrievalPreflightTests(unittest.TestCase): | ||
| 32 | + @classmethod | ||
| 33 | + def setUpClass(cls): | ||
| 34 | + cls.temporary = tempfile.TemporaryDirectory() | ||
| 35 | + cls.root = Path(cls.temporary.name) | ||
| 36 | + make_root(cls.root) | ||
| 37 | + directory = cls.root / "knowledge/model/inference/optimizations" | ||
| 38 | + directory.mkdir(parents=True) | ||
| 39 | + for slug, declaration, draft in [ | ||
| 40 | + ("bringup", scope(objective=["bringup"]), False), | ||
| 41 | + ("unknown", None, False), | ||
| 42 | + ("mla", scope(phase=["decode"], model_features={"all_of": ["mla"]}), False), | ||
| 43 | + ("draft", None, True), | ||
| 44 | + ]: | ||
| 45 | + metadata = {"type": "Optimization", "title": "范围夹具", "description": "检索协议的确定性夹具。", | ||
| 46 | + "tags": ["task.optimization", "topic.attention"], "status": "draft" if draft else "stable", | ||
| 47 | + "platforms": [] if draft else ["a3"], "sources": [{"resource": "https://example.com/preflight"}], | ||
| 48 | + "created_at": "2026-08-18T00:00:00Z", "updated_at": "2026-08-18T00:00:00Z"} | ||
| 49 | + if declaration is not None: | ||
| 50 | + metadata["scope"] = declaration | ||
| 51 | + (directory / (slug + ".md")).write_text("---\n" + yaml.safe_dump(metadata, allow_unicode=True) + "---\n\n# 范围夹具\n\n未命中查询词的正文。\n") | ||
| 52 | + built = run_index(cls.root, "build") | ||
| 53 | + if built.returncode: | ||
| 54 | + raise AssertionError(built.stderr) | ||
| 55 | + | ||
| 56 | + @classmethod | ||
| 57 | + def tearDownClass(cls): | ||
| 58 | + cls.temporary.cleanup() | ||
| 59 | + | ||
| 60 | + def case(self, slug="bringup", **overrides): | ||
| 61 | + return {"id": "fixture", "query": "absent-lexical-term", "expected": ["model/inference/optimizations/" + slug], | ||
| 62 | + "filters": {"domain": "model", "technology": "inference", "platform": ["a3"], "status": "stable"}, **overrides} | ||
| 63 | + | ||
| 64 | + def check(self, case): | ||
| 65 | + return preflight_cases({"cases": [case]}, {"repository": self.root}) | ||
| 66 | + | ||
| 67 | + def test_accepts_declared_match_without_claiming_lexical_or_human_match(self): | ||
| 68 | + report = self.check(self.case(scope=scope(objective=["bringup"]))) | ||
| 69 | + self.assertEqual("ready", report["status"]) | ||
| 70 | + self.assertEqual("match", report["cases"][0]["groups"][0]["alternatives"][0]["scope_match"]["state"]) | ||
| 71 | + self.assertIn("not relevance", report["note"]) | ||
| 72 | + | ||
| 73 | + def test_scope_conflict_rejects_invalid_positive_instead_of_dropping_it(self): | ||
| 74 | + report = self.check(self.case(scope=scope(objective=["latency"]))) | ||
| 75 | + self.assertEqual(["fixture"], report["failed_cases"]) | ||
| 76 | + self.assertEqual("scope_conflict", report["cases"][0]["groups"][0]["alternatives"][0]["reason"]) | ||
| 77 | + self.assertEqual(1, len(report["cases"])) | ||
| 78 | + | ||
| 79 | + def test_unknown_stays_eligible_but_explicit_absence_conflicts(self): | ||
| 80 | + declaration = scope(phase=["decode"], model_features={"none_of": ["mla"]}) | ||
| 81 | + unknown = self.check(self.case("unknown", scope=declaration)) | ||
| 82 | + self.assertEqual("ready", unknown["status"]) | ||
| 83 | + self.assertEqual("unknown", unknown["cases"][0]["groups"][0]["alternatives"][0]["scope_match"]["state"]) | ||
| 84 | + self.assertEqual("invalid_expectations", self.check(self.case("mla", scope=declaration))["status"]) | ||
| 85 | + | ||
| 86 | + def test_unlocated_draft_cannot_be_a_stable_platform_positive(self): | ||
| 87 | + report = self.check(self.case("draft")) | ||
| 88 | + self.assertEqual("invalid_expectations", report["status"]) | ||
| 89 | + self.assertEqual(["a3"], report["cases"][0]["request"]["filters"]["platforms"]) | ||
| 90 | + metadata = report["cases"][0]["groups"][0]["alternatives"][0]["metadata"] | ||
| 91 | + self.assertEqual("draft", metadata["status"]) | ||
| 92 | + self.assertEqual([], metadata["platforms"]) | ||
| 93 | + draft = self.case("draft", filters={"domain": "model", "technology": "inference", "status": "draft", "include_unlocated": True}) | ||
| 94 | + self.assertEqual("ready", self.check(draft)["status"]) | ||
| 95 | + self.assertEqual("invalid_expectations", self.check(self.case(filters={"platform": ["a2"]}))["status"]) | ||
| 96 | + | ||
| 97 | + def test_alternative_group_requires_one_eligible_member_and_all_mandatory_groups(self): | ||
| 98 | + prefix = "model/inference/optimizations/" | ||
| 99 | + alternatives = [[prefix + "draft", prefix + "unknown"]] | ||
| 100 | + self.assertEqual("ready", self.check(self.case(expected=[], expected_any=alternatives))["status"]) | ||
| 101 | + self.assertEqual("invalid_expectations", self.check(self.case("draft", expected_any=alternatives))["status"]) | ||
| 102 | + missing = self.check(self.case("missing")) | ||
| 103 | + self.assertEqual("missing_concept", missing["cases"][0]["groups"][0]["alternatives"][0]["reason"]) | ||
| 104 | + | ||
| 105 | + def test_malformed_or_duplicate_cases_fail_closed(self): | ||
| 106 | + with self.assertRaisesRegex(ValueError, "must be lists"): | ||
| 107 | + self.check(self.case(expected="model/inference/optimizations/unknown")) | ||
| 108 | + with self.assertRaisesRegex(ValueError, "duplicate"): | ||
| 109 | + preflight_cases({"cases": [self.case(), self.case()]}, {"repository": self.root}) | ||
| 110 | + with self.assertRaises(ValueError): | ||
| 111 | + self.check(self.case(scope={"schema": "unknown.schema", "any_of": [{"all_of": {}}]})) | ||
| 112 | + | ||
| 113 | + def test_scope_cannot_hide_in_generic_filters_in_either_execution_path(self): | ||
| 114 | + for key in ("scope", "scope_json", "scope_file", "scope-json", "scope-file", | ||
| 115 | + "scope-j", "scope_f"): | ||
| 116 | + for explicit in (False, True): | ||
| 117 | + with self.subTest(key=key, explicit_scope=explicit): | ||
| 118 | + case = self.case() | ||
| 119 | + case["filters"][key] = "hidden-scope" | ||
| 120 | + if explicit: | ||
| 121 | + case["scope"] = scope(phase=["decode"]) | ||
| 122 | + with self.assertRaisesRegex(ValueError, "only Scope input"): | ||
| 123 | + query_arguments(case) | ||
| 124 | + with self.assertRaisesRegex(ValueError, "only Scope input"): | ||
| 125 | + self.check(case) | ||
| 126 | + with patch("evals.run_retrieval.subprocess.run") as query: | ||
| 127 | + with self.assertRaisesRegex(ValueError, "only Scope input"): | ||
| 128 | + run_cases({"cases": [case]}, {"repository": self.root}) | ||
| 129 | + query.assert_not_called() | ||
| 130 | + | ||
| 131 | + def test_canonical_scope_and_legacy_arguments_remain_supported(self): | ||
| 132 | + legacy = self.case() | ||
| 133 | + arguments = query_arguments(legacy) | ||
| 134 | + self.assertNotIn("--scope-json", arguments) | ||
| 135 | + self.assertIn("--platform", arguments) | ||
| 136 | + scoped = {**legacy, "scope": scope(phase=["decode"])} | ||
| 137 | + arguments = query_arguments(scoped) | ||
| 138 | + self.assertEqual(1, arguments.count("--scope-json")) | ||
| 139 | + import json | ||
| 140 | + self.assertEqual(scoped["scope"], json.loads(arguments[arguments.index("--scope-json") + 1])) | ||
| 141 | + | ||
| 142 | + def test_preflight_is_read_only_and_does_not_build_missing_index(self): | ||
| 143 | + files = [p for p in self.root.rglob("*") if p.is_file()] | ||
| 144 | + before = {p: hashlib.sha256(p.read_bytes()).hexdigest() for p in files} | ||
| 145 | + self.assertEqual("ready", self.check(self.case())["status"]) | ||
| 146 | + self.assertEqual(before, {p: hashlib.sha256(p.read_bytes()).hexdigest() for p in files}) | ||
| 147 | + with tempfile.TemporaryDirectory() as temporary: | ||
| 148 | + root = Path(temporary) | ||
| 149 | + make_root(root) | ||
| 150 | + with self.assertRaises(ValueError): | ||
| 151 | + preflight_cases({"cases": [self.case()]}, {"repository": root}) | ||
| 152 | + self.assertFalse((root / "artifacts/indexes/knowledge.sqlite3").exists()) | ||
| 153 | + | ||
| 154 | + def test_preflight_only_does_not_silently_claim_quality_gate_passed(self): | ||
| 155 | + with tempfile.TemporaryDirectory() as temporary: | ||
| 156 | + cases = Path(temporary) / "cases.yaml" | ||
| 157 | + cases.write_text("cases: []\n") | ||
| 158 | + with patch("sys.argv", ["run_retrieval.py", "--cases", str(cases), "--preflight-only", "--quality-gate"]), redirect_stderr(io.StringIO()): | ||
| 159 | + self.assertEqual(2, main()) | ||
| 160 | + | ||
| 161 | + | ||
| 162 | +if __name__ == "__main__": | ||
| 163 | + unittest.main() | ||
| @@ -0,0 +1,217 @@ | |||
| 1 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | +# ---------------------------------------------------------------------------------------------------------- | ||
| 10 | + | ||
| 11 | +"""Optional partial Scope and trusted domain-handler extension boundaries.""" | ||
| 12 | + | ||
| 13 | +from __future__ import annotations | ||
| 14 | + | ||
| 15 | +import copy | ||
| 16 | +import json | ||
| 17 | +from pathlib import Path | ||
| 18 | +import subprocess | ||
| 19 | +import sys | ||
| 20 | +import tempfile | ||
| 21 | +import unittest | ||
| 22 | +from unittest.mock import patch | ||
| 23 | + | ||
| 24 | +import yaml | ||
| 25 | + | ||
| 26 | +from evals.test_retrieval_index import ROOT, make_root, run_index | ||
| 27 | +from governance.contracts.knowledge import Contract | ||
| 28 | +from governance.contracts.scope import ScopeRegistry | ||
| 29 | +from governance.contracts.scope_matchers import DIMENSION_MATCHERS, DimensionMatch, object_fields | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +class CapacityFixture: | ||
| 33 | + """Test-only non-enumerated handler; no production domain is preregistered.""" | ||
| 34 | + | ||
| 35 | + def __init__(self, definition, location): | ||
| 36 | + object_fields(definition, {"kind", "unit"}, location) | ||
| 37 | + if definition.get("unit") != "fixture_units": | ||
| 38 | + raise ValueError("fixture unit required") | ||
| 39 | + | ||
| 40 | + def normalize(self, value, *, query, location): | ||
| 41 | + del query | ||
| 42 | + value = object_fields(value, {"capacity"}, location) | ||
| 43 | + capacity = value.get("capacity") | ||
| 44 | + if type(capacity) is not int or capacity <= 0: | ||
| 45 | + raise ValueError("positive fixture capacity required") | ||
| 46 | + return {"capacity": capacity} | ||
| 47 | + | ||
| 48 | + def compare(self, query, card): | ||
| 49 | + if query is None: | ||
| 50 | + return DimensionMatch([{"state": "unknown", "reason": "fixture_missing_capacity"}]) | ||
| 51 | + if query["capacity"] < card["capacity"]: | ||
| 52 | + return DimensionMatch([{"state": "conflict", "reason": "fixture_insufficient_capacity"}]) | ||
| 53 | + return DimensionMatch() | ||
| 54 | + | ||
| 55 | + | ||
| 56 | +def fixture_scope(**conditions): | ||
| 57 | + return {"schema": "runtime.fixture.v1", "any_of": [{"all_of": conditions}]} | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +class ScopeExtensionTests(unittest.TestCase): | ||
| 61 | + def test_domain_kind_controls_configuration_values_and_matching(self): | ||
| 62 | + definitions = {"runtime.fixture.v1": { | ||
| 63 | + "description": "仅供测试:验证领域容量条件的校验与匹配。", "domains": ["runtime"], | ||
| 64 | + "dimensions": {"budget": {"kind": "fixture_capacity", "unit": "fixture_units"}}, | ||
| 65 | + }} | ||
| 66 | + with patch.dict(DIMENSION_MATCHERS, {"fixture_capacity": CapacityFixture}): | ||
| 67 | + registry = ScopeRegistry(definitions) | ||
| 68 | + card = fixture_scope(budget={"capacity": 4}) | ||
| 69 | + for capacity, expected in [(8, "match"), (4, "match"), (2, "conflict")]: | ||
| 70 | + query = registry.normalize(fixture_scope(budget={"capacity": capacity}), query=True) | ||
| 71 | + self.assertEqual(expected, registry.compare(query, card)["state"]) | ||
| 72 | + for capacity in [0, -1, True, "4", [4]]: | ||
| 73 | + with self.subTest(capacity=capacity), self.assertRaises(ValueError): | ||
| 74 | + registry.normalize(fixture_scope(budget={"capacity": capacity}), query=True) | ||
| 75 | + missing = copy.deepcopy(definitions) | ||
| 76 | + missing["runtime.fixture.v1"]["dimensions"]["mode"] = {"kind": "enum", "values": ["safe"]} | ||
| 77 | + with patch.dict(DIMENSION_MATCHERS, {"fixture_capacity": CapacityFixture}): | ||
| 78 | + registry = ScopeRegistry(missing) | ||
| 79 | + self.assertEqual("unknown", registry.compare(registry.normalize(fixture_scope(mode=["safe"])), card)["state"]) | ||
| 80 | + missing["runtime.fixture.v1"]["dimensions"]["budget"]["unit"] = "unregistered" | ||
| 81 | + with self.assertRaises(ValueError): | ||
| 82 | + ScopeRegistry(missing) | ||
| 83 | + with self.assertRaises(ValueError): | ||
| 84 | + ScopeRegistry(definitions) | ||
| 85 | + | ||
| 86 | + def test_registry_cannot_load_handlers_from_data(self): | ||
| 87 | + for kind in ["unregistered", "path.to.callable", "../handler", "__import__", []]: | ||
| 88 | + with self.subTest(kind=kind), self.assertRaises(ValueError): | ||
| 89 | + ScopeRegistry({"runtime.fixture.v1": { | ||
| 90 | + "description": "拒绝未登记处理器", "domains": ["runtime"], | ||
| 91 | + "dimensions": {"mode": {"kind": kind}}, | ||
| 92 | + }}) | ||
| 93 | + | ||
| 94 | + def test_matcher_must_return_valid_decision_reasons(self): | ||
| 95 | + for reasons in [None, [{}], [{"state": "match", "reason": "wrong"}], | ||
| 96 | + [{"state": "unknown", "reason": ""}]]: | ||
| 97 | + with self.subTest(reasons=reasons), self.assertRaises(ValueError): | ||
| 98 | + DimensionMatch(reasons) | ||
| 99 | + definitions = {"runtime.fixture.v1": { | ||
| 100 | + "description": "仅供测试:验证匹配处理器的非法返回值会被拒绝。", "domains": ["runtime"], | ||
| 101 | + "dimensions": {"budget": {"kind": "fixture_capacity", "unit": "fixture_units"}}, | ||
| 102 | + }} | ||
| 103 | + with patch.dict(DIMENSION_MATCHERS, {"fixture_capacity": CapacityFixture}): | ||
| 104 | + registry = ScopeRegistry(definitions) | ||
| 105 | + value = registry.normalize(fixture_scope(budget={"capacity": 1})) | ||
| 106 | + with patch.object(CapacityFixture, "compare", return_value=None), self.assertRaises(ValueError): | ||
| 107 | + registry.compare(value, value) | ||
| 108 | + | ||
| 109 | + def test_partial_scope_can_exclude_despite_another_unknown_dimension(self): | ||
| 110 | + registry = Contract(ROOT).scope_registry | ||
| 111 | + card = {"schema": "model.inference.v1", "any_of": [{"all_of": {"phase": ["prefill"]}}]} | ||
| 112 | + for phase, expected in [("prefill", "unknown"), ("decode", "conflict")]: | ||
| 113 | + query = {"schema": "model.inference.v1", "any_of": [{"all_of": { | ||
| 114 | + "phase": [phase], "execution_mode": ["ge_graph"], | ||
| 115 | + }}]} | ||
| 116 | + self.assertEqual(expected, registry.compare(registry.normalize(query, query=True), card)["state"]) | ||
| 117 | + self.assertEqual("unknown", registry.compare(registry.normalize(card, query=True), None)["state"]) | ||
| 118 | + | ||
| 119 | + def test_bindings_are_explicit_and_do_not_spread_across_routes(self): | ||
| 120 | + registry = ScopeRegistry({"runtime.fixture.v1": { | ||
| 121 | + "description": "仅供测试:验证 Schema 只适用于明确登记的领域和路由。", | ||
| 122 | + "domains": ["runtime"], "routes": ["model/inference"], | ||
| 123 | + "dimensions": {"mode": {"kind": "enum", "values": ["sync"]}}, | ||
| 124 | + }}) | ||
| 125 | + self.assertTrue(registry.applies_to("runtime.fixture.v1", "runtime", None)) | ||
| 126 | + self.assertTrue(registry.applies_to("runtime.fixture.v1", "model", "inference")) | ||
| 127 | + self.assertFalse(registry.applies_to("runtime.fixture.v1", "model", "training")) | ||
| 128 | + self.assertFalse(registry.applies_to("runtime.fixture.v1", "runtime", "inference")) | ||
| 129 | + for bindings in [{}, {"domains": []}, {"domains": "runtime"}, {"routes": []}]: | ||
| 130 | + with self.subTest(bindings=bindings), self.assertRaises(ValueError): | ||
| 131 | + ScopeRegistry({"runtime.fixture.v1": {"description": "非法绑定", "dimensions": { | ||
| 132 | + "mode": {"kind": "enum", "values": ["sync"]}}, **bindings}}) | ||
| 133 | + | ||
| 134 | + | ||
| 135 | +class DirectDomainScopeTests(unittest.TestCase): | ||
| 136 | + def setUp(self): | ||
| 137 | + self.temporary = tempfile.TemporaryDirectory() | ||
| 138 | + self.addCleanup(self.temporary.cleanup) | ||
| 139 | + self.root = Path(self.temporary.name) | ||
| 140 | + make_root(self.root) | ||
| 141 | + self.path = self.root / "governance/schemas/registries.yaml" | ||
| 142 | + self.data = yaml.safe_load(self.path.read_text()) | ||
| 143 | + self.data["scope_schemas"]["runtime.fixture.v1"] = { | ||
| 144 | + "description": "仅供测试:验证直接领域的 Scope 注册与检索。", "domains": ["runtime"], | ||
| 145 | + "dimensions": {"mode": {"kind": "enum", "values": ["sync", "async"]}}, | ||
| 146 | + } | ||
| 147 | + self.save_registry() | ||
| 148 | + | ||
| 149 | + def save_registry(self): | ||
| 150 | + self.path.write_text(yaml.safe_dump(self.data, allow_unicode=True)) | ||
| 151 | + | ||
| 152 | + def test_domain_registration_rejects_unknown_or_non_direct_domains(self): | ||
| 153 | + for domain in ["missing", "model", "ops"]: | ||
| 154 | + self.data["scope_schemas"]["runtime.fixture.v1"]["domains"] = [domain] | ||
| 155 | + self.save_registry() | ||
| 156 | + with self.subTest(domain=domain), self.assertRaises(ValueError): | ||
| 157 | + Contract(self.root) | ||
| 158 | + | ||
| 159 | + def test_direct_scope_does_not_bypass_route_or_profile_boundaries(self): | ||
| 160 | + contract = Contract(self.root) | ||
| 161 | + metadata = { | ||
| 162 | + "type": "Concept", "title": "路径边界测试卡", "description": "验证 Scope 不会绕过路由或 Profile 限制。", | ||
| 163 | + "tags": ["task.lookup", "topic.graph_execution"], "status": "stable", "platforms": ["a3"], | ||
| 164 | + "sources": [{"resource": "https://example.com/direct-scope-fixture"}], | ||
| 165 | + "created_at": "2026-08-18T00:00:00Z", "updated_at": "2026-08-18T00:00:00Z", | ||
| 166 | + "scope": fixture_scope(mode=["sync"]), | ||
| 167 | + } | ||
| 168 | + self.assertEqual([], contract.validate_card(Path("knowledge/runtime/concepts/fixture.md"), metadata, "# 路径边界测试卡\n")) | ||
| 169 | + issues = contract.validate_card(Path("knowledge/model/inference/concepts/fixture.md"), metadata, "# 路径边界测试卡\n") | ||
| 170 | + self.assertIn("scope.invalid", {i.code for i in issues}) | ||
| 171 | + metadata["type"] = "API" | ||
| 172 | + issues = contract.validate_card(Path("knowledge/runtime/apis/fixture.md"), metadata, "# 路径边界测试卡\n") | ||
| 173 | + self.assertTrue(any("scope" in i.message for i in issues)) | ||
| 174 | + | ||
| 175 | + def test_external_root_cannot_supply_executable_handler_code(self): | ||
| 176 | + handler = self.root / "governance/contracts/scope_matchers.py" | ||
| 177 | + handler.write_text(handler.read_text() + "\nraise RuntimeError('external-handler-was-executed')\n") | ||
| 178 | + result = run_index(self.root, "build") | ||
| 179 | + self.assertEqual(2, result.returncode) | ||
| 180 | + self.assertIn("does not match", result.stderr) | ||
| 181 | + self.assertNotIn("external-handler-was-executed", result.stderr) | ||
| 182 | + | ||
| 183 | + def test_direct_domain_scope_runs_through_index_and_query(self): | ||
| 184 | + directory = self.root / "knowledge/runtime/concepts" | ||
| 185 | + directory.mkdir(parents=True) | ||
| 186 | + for slug, mode in [("sync", "sync"), ("async", "async"), ("unknown", None)]: | ||
| 187 | + metadata = { | ||
| 188 | + "type": "Concept", "title": "DirectScopeFixture", "description": "验证直接领域查询排除冲突卡、保留未知卡。", | ||
| 189 | + "tags": ["task.lookup", "topic.graph_execution"], "status": "stable", "platforms": ["a3"], | ||
| 190 | + "sources": [{"resource": "https://example.com/direct-scope-fixture"}], | ||
| 191 | + "created_at": "2026-08-18T00:00:00Z", "updated_at": "2026-08-18T00:00:00Z", | ||
| 192 | + } | ||
| 193 | + if mode is not None: | ||
| 194 | + metadata["scope"] = fixture_scope(mode=[mode]) | ||
| 195 | + (directory / f"{slug}.md").write_text("---\n" + yaml.safe_dump(metadata) + "---\n\n# DirectScopeFixture\n") | ||
| 196 | + built = run_index(self.root, "build") | ||
| 197 | + self.assertEqual(0, built.returncode, built.stderr) | ||
| 198 | + command = [sys.executable, str(ROOT / ".agents/skills/knowledge-query/scripts/knowledge_query.py"), | ||
| 199 | + "--knowledge-root", str(self.root), "search", "--query", "DirectScopeFixture", | ||
| 200 | + "--domain", "runtime", "--platform", "a3", "--match", "literal"] | ||
| 201 | + plain = subprocess.run(command, capture_output=True, text=True) | ||
| 202 | + self.assertEqual(0, plain.returncode, plain.stderr) | ||
| 203 | + self.assertEqual(3, json.loads(plain.stdout)["count"]) | ||
| 204 | + result = subprocess.run(command + ["--scope-json", json.dumps(fixture_scope(mode=["sync"]))], | ||
| 205 | + capture_output=True, text=True) | ||
| 206 | + self.assertEqual(0, result.returncode, result.stderr) | ||
| 207 | + payload = json.loads(result.stdout) | ||
| 208 | + self.assertEqual({"runtime/concepts/sync", "runtime/concepts/unknown"}, {h["path"] for h in payload["results"]}) | ||
| 209 | + self.assertEqual(1, payload["scope_filter"]["excluded_count"]) | ||
| 210 | + self.assertEqual(["match", "unknown"], [h["scope_match"]["state"] for h in payload["results"]]) | ||
| 211 | + bad = subprocess.run(command + ["--scope-json", json.dumps(fixture_scope(phase=["prefill"]))], | ||
| 212 | + capture_output=True, text=True) | ||
| 213 | + self.assertEqual(2, bad.returncode) | ||
| 214 | + | ||
| 215 | + | ||
| 216 | +if __name__ == "__main__": | ||
| 217 | + unittest.main() | ||
| @@ -397,7 +397,14 @@ class ScopeGovernanceWorkflowTests(unittest.TestCase): | |||
| 397 | value["unexpected_field"] = True | 397 | value["unexpected_field"] = True |
| 398 | self.assertIn("frontmatter.schema", {item.code for item in self.contract.validate_card(self.relative, value, self.body)}) | 398 | self.assertIn("frontmatter.schema", {item.code for item in self.contract.validate_card(self.relative, value, self.body)}) |
| 399 | 399 | ||
| 400 | - def test_legacy_experience_paths_remain_valid_until_knowledge_migration(self): | 400 | + def test_legacy_experience_paths_remain_valid_when_not_retired_in_registry(self): |
| 401 | + # The Scope foundation remains compatible before a route opts into retirement. | ||
| 402 | + # Production model/inference now enables that separate migration rule. | ||
| 403 | + path = self.root / "governance/schemas/registries.yaml" | ||
| 404 | + registry = yaml.safe_load(path.read_text()) | ||
| 405 | + registry["paths"].pop("forbidden_sections", None) | ||
| 406 | + path.write_text(yaml.safe_dump(registry, allow_unicode=True)) | ||
| 407 | + self.contract = Contract(self.root) | ||
| 401 | for section, kind in [("guides/experience/interventions", "Guide"), | 408 | for section, kind in [("guides/experience/interventions", "Guide"), |
| 402 | ("optimizations/experience/rules", "Optimization")]: | 409 | ("optimizations/experience/rules", "Optimization")]: |
| 403 | relative = Path("knowledge/model/inference") / section | 410 | relative = Path("knowledge/model/inference") / section |
| @@ -22,6 +22,12 @@ ID_RE = re.compile(r"^[a-z0-9_]+$") | |||
| 22 | FREELY_NESTED_PROFILES = {"apis", "concepts", "examples", "guides", "optimizations"} | 22 | FREELY_NESTED_PROFILES = {"apis", "concepts", "examples", "guides", "optimizations"} |
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | +def validate_topic_sections(contract: Contract, domain, route, profile, sections) -> None: | ||
| 26 | + forbidden = contract.forbidden_sections.get(f"{domain}/{route}", {}).get(profile, []) | ||
| 27 | + if any(section in forbidden for section in sections): | ||
| 28 | + raise ValueError(f"{domain}/{route}/{profile} contains a retired section") | ||
| 29 | + | ||
| 30 | + | ||
| 25 | def check(contract: Contract, document, context) -> list[Issue]: | 31 | def check(contract: Contract, document, context) -> list[Issue]: |
| 26 | del contract, context | 32 | del contract, context |
| 27 | if ( | 33 | if ( |
| @@ -73,6 +79,7 @@ def derive_card_path(contract: Contract, relative: Path) -> PathInfo: | |||
| 73 | raise ValueError("path must be domain/type or domain/technology-or-scope/type") | 79 | raise ValueError("path must be domain/type or domain/technology-or-scope/type") |
| 74 | 80 | ||
| 75 | suffix = parts[type_index + 1 : -1] | 81 | suffix = parts[type_index + 1 : -1] |
| 82 | + validate_topic_sections(contract, domain, route, card_type, suffix) | ||
| 76 | if card_type == "operators": | 83 | if card_type == "operators": |
| 77 | if len(suffix) != 3 or suffix[0] not in contract.operator_call_modes: | 84 | if len(suffix) != 3 or suffix[0] not in contract.operator_call_modes: |
| 78 | allowed = "|".join(sorted(contract.operator_call_modes)) | 85 | allowed = "|".join(sorted(contract.operator_call_modes)) |
| @@ -140,6 +140,29 @@ class Contract: | |||
| 140 | self.operator_families = set(self.operator_categories) | 140 | self.operator_families = set(self.operator_categories) |
| 141 | self.runbook_areas = self._string_set("runbook_areas") | 141 | self.runbook_areas = self._string_set("runbook_areas") |
| 142 | self._validate_registry_contract() | 142 | self._validate_registry_contract() |
| 143 | + self.forbidden_sections = self.path_values.get("forbidden_sections", {}) | ||
| 144 | + if not isinstance(self.forbidden_sections, dict): | ||
| 145 | + raise ValueError("paths.forbidden_sections must be a mapping") | ||
| 146 | + for binding, profiles in self.forbidden_sections.items(): | ||
| 147 | + parts = binding.split("/") if isinstance(binding, str) else [] | ||
| 148 | + if ( | ||
| 149 | + len(parts) != 2 | ||
| 150 | + or parts[0] not in self.domains | ||
| 151 | + or parts[1] not in self.routes | ||
| 152 | + or parts[0] not in self.route_domains(parts[1]) | ||
| 153 | + or not isinstance(profiles, dict) | ||
| 154 | + or not profiles | ||
| 155 | + ): | ||
| 156 | + raise ValueError("paths.forbidden_sections requires registered domain/route bindings") | ||
| 157 | + for profile, sections in profiles.items(): | ||
| 158 | + if ( | ||
| 159 | + profile not in self.types | ||
| 160 | + or not isinstance(sections, list) | ||
| 161 | + or not sections | ||
| 162 | + or any(not isinstance(value, str) or not ID_RE.fullmatch(value) for value in sections) | ||
| 163 | + or len(set(sections)) != len(sections) | ||
| 164 | + ): | ||
| 165 | + raise ValueError("paths.forbidden_sections requires registered profiles and unique section IDs") | ||
| 143 | self.scope_registry = ScopeRegistry(self.registries.get("scope_schemas", {})) | 166 | self.scope_registry = ScopeRegistry(self.registries.get("scope_schemas", {})) |
| 144 | for namespace, definition in self.scope_registry.definitions.items(): | 167 | for namespace, definition in self.scope_registry.definitions.items(): |
| 145 | for domain in definition.get("domains", []): | 168 | for domain in definition.get("domains", []): |
| @@ -282,6 +282,10 @@ local_sources: | |||
| 282 | operator: knowledge/ops/ascendc/operators/aclnn/transformer/mc2/allto_all_matmul.md | 282 | operator: knowledge/ops/ascendc/operators/aclnn/transformer/mc2/allto_all_matmul.md |
| 283 | 283 | ||
| 284 | paths: | 284 | paths: |
| 285 | + forbidden_sections: | ||
| 286 | + model/inference: | ||
| 287 | + guides: [experience] | ||
| 288 | + optimizations: [experience] | ||
| 285 | operator_call_modes: [aclnn, direct] | 289 | operator_call_modes: [aclnn, direct] |
| 286 | operator_categories: | 290 | operator_categories: |
| 287 | cv: [image, objdetect] | 291 | cv: [image, objdetect] |
| @@ -100,14 +100,12 @@ scope: | |||
| 100 | 100 | ||
| 101 | | 查询已确认条件 | 卡片要求 `parallelism.none_of: [cp]` 时 | | 101 | | 查询已确认条件 | 卡片要求 `parallelism.none_of: [cp]` 时 | |
| 102 | | --- | --- | | 102 | | --- | --- | |
| 103 | -| `parallelism.all_of: [cp]` | `conflict`,明确冲突 | | 103 | +| `parallelism.all_of: [cp]` | `conflict`,明确冲突并排除 | |
| 104 | | `parallelism.none_of: [cp]` | 满足这一项限制;其他维度继续核对 | | 104 | | `parallelism.none_of: [cp]` | 满足这一项限制;其他维度继续核对 | |
| 105 | | 只声明 `parallelism.all_of: [tp]` | `unknown`,TP 不证明 CP 已关闭 | | 105 | | 只声明 `parallelism.all_of: [tp]` | `unknown`,TP 不证明 CP 已关闭 | |
| 106 | | 没有声明 `parallelism` | `unknown`,CP 状态待确认 | | 106 | | 没有声明 `parallelism` | `unknown`,CP 状态待确认 | |
| 107 | 107 | ||
| 108 | -Scope Contract 的比较结果为 `match`、`unknown` 或 `conflict`。缺少卡片 Scope、缺少必要条件或不同的已注册 namespace 均保留未知,非法 Scope 报错。匹配表示声明条件相容,不替代正文、来源与版本核对,也不证明性能已经验证。 | 108 | +无显式查询 Scope 时,Query 保留原有过滤和排序;显式传入确认后的条件时,只排除 `conflict`,保留 `match` 和 `unknown`,并回显原因、场景分支及待确认前提。相关旧卡片和其他已注册 namespace 的相关卡片保留为未知项,不能跨 schema 猜测。非法 Scope 报错,不降级为未知。Scope 条件匹配不替代正文、来源和版本核对,也不证明已做性能验证。Query 的参数与返回结构见其 Skill 文档。 |
| 109 | - | ||
| 110 | -当前基础治理在加载卡片、Lint 和构图时校验该字段,并提供统一的三态比较接口。Query 的现有公开接口不接受场景 Scope 过滤参数;卡片声明 Scope 不代表检索已启用该过滤。检索参数及结果结构以 Query Skill 的实际能力为准,无 Scope 的原查询流程保持不变。 | ||
| 111 | 109 | ||
| 112 | ## 生命周期与信任 | 110 | ## 生命周期与信任 |
| 113 | 111 | ||
| @@ -57,6 +57,10 @@ Registry 允许某种组合,只表示工具能够识别它,不表示仓库 | |||
| 57 | 57 | ||
| 58 | 不单设 `patterns/`:原理进入 `concepts/`,完整算子进入 `operators/`,性能方法进入 `optimizations/`,处置步骤进入 `runbooks/`。版本差异由适用范围和生命周期表达,不建立 `migrations/` Profile。 | 58 | 不单设 `patterns/`:原理进入 `concepts/`,完整算子进入 `operators/`,性能方法进入 `optimizations/`,处置步骤进入 `runbooks/`。版本差异由适用范围和生命周期表达,不建立 `migrations/` Profile。 |
| 59 | 59 | ||
| 60 | +模型推理允许同一主题有多个知识视图,但同一条可复用规则只维护一份规范正文:Concept 负责定义与机制,Optimization 负责触发条件与决策,Guide 串联完整任务。Case 补充特定条件下的改动与证据,不能重新维护通用规则;反模式形成完整诊断、修复和验证闭环时由 Runbook 承载。新增和实质更新前先按 Query/Ingest 工作流查重,旧卡的归并与路径迁移先形成逐卡清单再处理。Prefill/Decode 等场景用 Scope 表达,不据此增加一套阶段目录。 | ||
| 61 | + | ||
| 62 | +`model/inference` 的 Guide 按任务组织为 `bringup`、`deployment`、`integration`、`models`、`optimization`;Optimization 按职责组织为 `rules`、`cases`、`anti_patterns`、`benchmark_observations`,这些仍共享原有 Profile。Registry 的 `paths.forbidden_sections` 声明哪些 domain/route/Profile 已停用某个 section;Contract 对卡片和索引采用同一规则。目前模型推理的 Guide 与 Optimization 均不再接受 `experience` 层,其他领域不继承这一限制。 | ||
| 63 | + | ||
| 60 | ## Profile 附加层级 | 64 | ## Profile 附加层级 |
| 61 | 65 | ||
| 62 | `concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可以按真实主题增加任意深度的小写 `snake_case` section,用来保持大型官方参考集可浏览。section 只是导航,不是新的 domain、route 或 Profile,也不进入 Frontmatter;每一级都必须有完整的 `index.md`。禁止使用来源批次、日期或发布版本作为 section。 | 66 | `concepts`、`apis`、`examples`、`guides` 和 `optimizations` 可以按真实主题增加任意深度的小写 `snake_case` section,用来保持大型官方参考集可浏览。section 只是导航,不是新的 domain、route 或 Profile,也不进入 Frontmatter;每一级都必须有完整的 `index.md`。禁止使用来源批次、日期或发布版本作为 section。 |
| @@ -28,8 +28,8 @@ Contract 直接消费这些声明,只保留现有的可加载性和 `okf_type` | |||
| 28 | | `domains` | 顶层领域、允许直辖的 `direct_types` 及是否 `precreate_routes` | 路径校验、domain 过滤、协作骨架 | | 28 | | `domains` | 顶层领域、允许直辖的 `direct_types` 及是否 `precreate_routes` | 路径校验、domain 过滤、协作骨架 | |
| 29 | | `technologies` | DSL、语言、框架或引擎及允许的 domain | route、technology 和跨技术端点校验 | | 29 | | `technologies` | DSL、语言、框架或引擎及允许的 domain | route、technology 和跨技术端点校验 | |
| 30 | | `scopes` | `shared`、平台、工具链、推理/训练等非技术 route | route、scope 和 shared 过滤 | | 30 | | `scopes` | `shared`、平台、工具链、推理/训练等非技术 route | route、scope 和 shared 过滤 | |
| 31 | -| `scope_schemas` | 场景 Scope namespace、适用路径、维度类型、值与别名 | 卡片适用条件校验及 Contract 三态比较;与路径 route 的 `scopes` 分区独立 | | 31 | +| `scope_schemas` | 场景 Scope namespace、绑定路由、维度类型、规范值和确定性别名 | 适用条件校验、三态匹配和显式查询过滤;与路径 route 的 `scopes` 分区独立 | |
| 32 | -| `paths` | Operator call mode、family→category 映射和 Runbook area | 受控附加层级校验 | | 32 | +| `paths` | Operator call mode、family→category 映射、Runbook area 和按 domain/route/Profile 绑定的 `forbidden_sections` | 受控附加层级与已停用 section 校验 | |
| 33 | | `platforms` | 卡片适用平台 ID | Frontmatter 校验和 platform 过滤 | | 33 | | `platforms` | 卡片适用平台 ID | Frontmatter 校验和 platform 过滤 | |
| 34 | | `tags` | task、topic、paradigm 三个维度的规范标签 | 标签校验、任务映射、过滤和召回 | | 34 | | `tags` | task、topic、paradigm 三个维度的规范标签 | 标签校验、任务映射、过滤和召回 | |
| 35 | 35 | ||
| @@ -47,19 +47,19 @@ technology 与 scope 的 ID 不得重叠;每个 route 必须声明允许出现 | |||
| 47 | 47 | ||
| 48 | 新增注册值必须同时满足:现有值无法准确表达需求、归属和语义明确、有真实知识或确定性消费者即将使用,并在同一变更补充接受/拒绝测试和检索回归。若属于 `ops` 或 `model`,还要同步其 route 结构入口;其他领域由维护者随真实内容创建路径。不要只为来源别名、任意占位或一次性任务扩充 Registry。 | 48 | 新增注册值必须同时满足:现有值无法准确表达需求、归属和语义明确、有真实知识或确定性消费者即将使用,并在同一变更补充接受/拒绝测试和检索回归。若属于 `ops` 或 `model`,还要同步其 route 结构入口;其他领域由维护者随真实内容创建路径。不要只为来源别名、任意占位或一次性任务扩充 Registry。 |
| 49 | 49 | ||
| 50 | -Scope 的公共对象和场景分支形状由 Frontmatter Schema 定义;各 namespace 的适用范围、维度配置、值与别名由 `scope_schemas` 独立定义。`contracts/scope.py` 组织校验与三态匹配,`contracts/scope_matchers.py` 的受控处理器表按维度 `kind` 分派定义校验、值归一化和比较;卡片加载、Ingest、Lint 与构图共用这一路径,不各自定义语法。Contract 提供比较接口,Query 的场景 Scope 参数及过滤接入由其公开消费能力决定。 | 50 | +Scope 的公共对象和场景分支形状由 Frontmatter Schema 定义;各 namespace 的适用范围、维度配置、值与别名由 `scope_schemas` 独立定义。`contracts/scope.py` 组织校验与三态匹配,`contracts/scope_matchers.py` 的受控处理器表按维度 `kind` 分派定义校验、值归一化和比较;卡片加载、Ingest、Lint、索引、构图和 Query 共用这一路径,不各自定义语法;显式查询条件由同一 Contract 规范化和比较。 |
| 51 | 51 | ||
| 52 | 现有处理器为 `enum` 与 `required_set`。枚举使用独占 `[any]`,必需集合使用非空 `all_of`,并由维度的布尔配置 `query_none_of`、`card_none_of` 分别控制查询与卡片能否声明非空 `none_of`。允许否定的集合可只写 `none_of`,或与 `all_of` 同时声明;正反集合不得相交,缺失成员不表示未启用。当前模型推理的 `parallelism` 对卡片和查询均允许否定,`model_features` 仍只允许查询否定,其他维度不自动获得此权限。这些模型字段和权限不约束其他领域的维度设计。 | 52 | 现有处理器为 `enum` 与 `required_set`。枚举使用独占 `[any]`,必需集合使用非空 `all_of`,并由维度的布尔配置 `query_none_of`、`card_none_of` 分别控制查询与卡片能否声明非空 `none_of`。允许否定的集合可只写 `none_of`,或与 `all_of` 同时声明;正反集合不得相交,缺失成员不表示未启用。当前模型推理的 `parallelism` 对卡片和查询均允许否定,`model_features` 仍只允许查询否定,其他维度不自动获得此权限。这些模型字段和权限不约束其他领域的维度设计。 |
| 53 | 53 | ||
| 54 | namespace 至少声明一种非空适用范围:`routes` 绑定合法的 `domain/route`;`domains` 只绑定 Registry 允许直接承载 Profile 的领域及其无 route 路径,例如 Runtime 的直接 Concept 或 Runbook。两者可同时声明,但不会隐式扩展到未列出的 route;Profile 自身仍须允许 `scope`。新增领域应随真实过滤需求登记 namespace 和回归,不为未来领域建立占位 Schema。未注册 schema、处理器类型、非法值或错误路径绑定失败关闭。 | 54 | namespace 至少声明一种非空适用范围:`routes` 绑定合法的 `domain/route`;`domains` 只绑定 Registry 允许直接承载 Profile 的领域及其无 route 路径,例如 Runtime 的直接 Concept 或 Runbook。两者可同时声明,但不会隐式扩展到未列出的 route;Profile 自身仍须允许 `scope`。新增领域应随真实过滤需求登记 namespace 和回归,不为未来领域建立占位 Schema。未注册 schema、处理器类型、非法值或错误路径绑定失败关闭。 |
| 55 | 55 | ||
| 56 | -领域新增匹配语义时,在当前安装的 Contract 包内增加确定性处理器并登记到受控表,使用 `normalize` 校验及规范化卡片/查询条件,使用 `compare` 返回 `DimensionMatch`。处理器独立检查其配置与值形状,可以使用不同于枚举、集合的条件结构;必须以明确原因报告未知或冲突,不能把缺证据当作匹配。公共聚合仍保持场景内 AND、场景间 OR、任一明确冲突不被未知掩盖,且不跨 namespace 推断。处理器不得联网、执行上游源码或写知识卡;Registry、知识卡、查询参数及外部知识根不能指定 Python 模块、文件路径或执行任意表达式。扩展必须覆盖接受、拒绝、未知、原有语义兼容及实际消费者;修改同版本 Contract 后由维护者重建索引。 | 56 | +领域新增匹配语义时,在当前安装的 Contract 包内增加确定性处理器并登记到受控表,使用 `normalize` 校验及规范化卡片/查询条件,使用 `compare` 返回 `DimensionMatch`。处理器独立检查其配置与值形状,可以使用不同于枚举、集合的条件结构;必须以明确原因报告未知或冲突,不能把缺证据当作匹配。公共聚合仍保持场景内 AND、场景间 OR、任一明确冲突不被未知掩盖,且不跨 namespace 推断。处理器不得联网、执行上游源码或写知识卡;Registry、知识卡、查询参数及外部知识根不能指定 Python 模块、文件路径或执行任意表达式。扩展必须覆盖接受、拒绝、未知、原有语义兼容及检索过滤;修改同版本 Contract 后由维护者重建索引。 |
| 57 | 57 | ||
| 58 | -Scope 本身始终可选,可只写有来源依据且有过滤价值的部分条件。Lint 不把缺少 Scope 或缺少维度视为质量错误,不以填写率验收;当前 Query 仍使用原有过滤与排序,不能从字段存在推断场景过滤已接入。 | 58 | +Scope 本身始终可选,可只写有来源依据且有过滤价值的部分条件。Lint 不把缺少 Scope 或缺少维度视为质量错误,检索不会因填写完整而增加分数;实际效果用错误候选是否被排除、相关结果是否保留来验证,不设全量填写要求。 |
| 59 | 59 | ||
| 60 | 集合匹配分别检查卡片要求启用与未启用的成员。查询明确关闭必需成员、或明确启用卡片禁止的成员时为冲突;任何必需状态未确认时为未知。只要任一必要条件明确冲突,就不能用另一项未知掩盖冲突;多个场景仍逐对匹配,不跨分支合并条件。 | 60 | 集合匹配分别检查卡片要求启用与未启用的成员。查询明确关闭必需成员、或明确启用卡片禁止的成员时为冲突;任何必需状态未确认时为未知。只要任一必要条件明确冲突,就不能用另一项未知掩盖冲突;多个场景仍逐对匹配,不跨分支合并条件。 |
| 61 | 61 | ||
| 62 | -Registry 或 Contract 更新后,原索引按治理指纹失效,必须由维护者重建;Query 不自行重建。旧 receipt 仍按完整 provenance 校验,不跨治理版本自动放行。回退前先移除依赖新 Schema 的声明,再还原同版本治理文件并重建索引;不得让旧消费者静默忽略无法识别的条件。 | 62 | +只使用原有正向集合的卡片和查询保留原语义。Registry 或 Contract 更新后,原索引按治理指纹失效,必须由维护者重建;Query 不自行重建。旧 receipt 仍按完整 provenance 校验,不跨治理版本自动放行。回退时先撤下新增卡片否定声明,再还原同版本 Registry/Contract 并重建索引,不能让旧消费者忽略无法识别的条件。 |
| 63 | 63 | ||
| 64 | ## 执行关系 | 64 | ## 执行关系 |
| 65 | 65 | ||
| @@ -112,6 +112,8 @@ JSON Schema 只能检查单个对象的形状,不能独立完成: | |||
| 112 | 112 | ||
| 113 | 后一类问题必须由贡献者和 reviewer 判断。机器声明、Contract 和 Spec 不一致时,应在同一变更修复事实源、执行逻辑、文档和回归,而不是选择其中一份绕过。 | 113 | 后一类问题必须由贡献者和 reviewer 判断。机器声明、Contract 和 Spec 不一致时,应在同一变更修复事实源、执行逻辑、文档和回归,而不是选择其中一份绕过。 |
| 114 | 114 | ||
| 115 | +推理 Scope 的真实语料回归独立检查显式过滤、未知保留、冲突审计和原算子查询,保留原检索语料及其质量阈值。用例中的预期机器状态只证明契约行为;人工适用性评测仍需结合正文和来源单独判定,不能用机器状态替代人工标签或宣称 A/B 改善。 | ||
| 116 | + | ||
| 115 | ## 修改规则 | 117 | ## 修改规则 |
| 116 | 118 | ||
| 117 | | 变更 | 至少同步 | | 119 | | 变更 | 至少同步 | |
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | 具体执行接口只在各 Skill 自身的 `SKILL.md` 和 `references/` 中维护,本页只定义仓库级行为与协作边界。 | 5 | 具体执行接口只在各 Skill 自身的 `SKILL.md` 和 `references/` 中维护,本页只定义仓库级行为与协作边界。 |
| 6 | 6 | ||
| 7 | +场景 Scope 是可选输入和卡片声明。Ingest 只接收来源支持的卡片条件;Query 仅使用调用方显式确认的条件排除冲突,保留未知并解释待确认前提;Lint 校验结构、注册值和路由。三者共用治理 Contract,Scope 不增加联网、回写或自动重建索引的权限,未传 Scope 的旧查询保持原行为。 | ||
| 8 | + | ||
| 7 | ## 分工 | 9 | ## 分工 |
| 8 | 10 | ||
| 9 | | Skill | 输入 | 输出 | 写入边界 | | 11 | | Skill | 输入 | 输出 | 写入边界 | |
| @@ -103,5 +103,5 @@ A2/A3 栏写 SIMD,表示当前公开 tuning 文档没有为它们承诺 SIMT | |||
| 103 | - [PRINTF 扰动时序:去 PRINTF 复验再定论](../../../../ops/ascendc/runbooks/runtime/printf_perturbs_timing_verify_without_printf.md):那卡讲打印如何掩盖真实竞态、制造幽灵异常;本卡讲打印落在测量区内如何污染周期数,两者的证伪手段一致——去掉打印再复验。 | 103 | - [PRINTF 扰动时序:去 PRINTF 复验再定论](../../../../ops/ascendc/runbooks/runtime/printf_perturbs_timing_verify_without_printf.md):那卡讲打印如何掩盖真实竞态、制造幽灵异常;本卡讲打印落在测量区内如何污染周期数,两者的证伪手段一致——去掉打印再复验。 |
| 104 | - [Kernel printf 无输出诊断](../runtime/kernel_printf_no_output_diagnosis.md):printf 无输出的五步定位;本卡第 5 节只处理 `asc_time_stamp` 无输出,printf 的输出链路问题走该卡。 | 104 | - [Kernel printf 无输出诊断](../runtime/kernel_printf_no_output_diagnosis.md):printf 无输出的五步定位;本卡第 5 节只处理 `asc_time_stamp` 无输出,printf 的输出链路问题走该卡。 |
| 105 | - [PrintTimeStamp(API 卡)](../../../../ops/ascendc/apis/simd_api/basic_api/debug_interface/onboard_print/print_time_stamp.md):另一个时间戳打点接口的事实卡,需要时按该卡使用,本卡不替它推导行为。 | 105 | - [PrintTimeStamp(API 卡)](../../../../ops/ascendc/apis/simd_api/basic_api/debug_interface/onboard_print/print_time_stamp.md):另一个时间戳打点接口的事实卡,需要时按该卡使用,本卡不替它推导行为。 |
| 106 | -- [MTP / 投机解码 decode 计时:拆「每 step 时延」与「每等效 token 时延」两口径分报](../../../../model/inference/optimizations/experience/rules/mtp_decode_timing_two_metric_split.md):推理侧的**计时口径拆分**(框架层,解决"一个数糊了两件事");本卡是 Kernel 内**单段测量的口径与可信度**,一个在框架层、一个在核内,互不替代,可组合使用。 | 106 | +- [MTP / 投机解码 decode 计时:拆「每 step 时延」与「每等效 token 时延」两口径分报](../../../../model/inference/optimizations/rules/mtp_decode_timing_two_metric_split.md):推理侧的**计时口径拆分**(框架层,解决"一个数糊了两件事");本卡是 Kernel 内**单段测量的口径与可信度**,一个在框架层、一个在核内,互不替代,可组合使用。 |
| 107 | -- [昇腾性能测量失真陷阱:不代表真实访存的测量输入·折算前被截断的度量口径](../../../../model/inference/optimizations/experience/anti_patterns/perf_measurement_distortion_input_and_metric.md):讲测量输入与折算口径造成的系统性失真;本卡补充"测量区内部被调试输出污染"这一类。 | 107 | +- [昇腾性能测量失真陷阱:不代表真实访存的测量输入·折算前被截断的度量口径](../../../../model/inference/optimizations/anti_patterns/perf_measurement_distortion_input_and_metric.md):讲测量输入与折算口径造成的系统性失真;本卡补充"测量区内部被调试输出污染"这一类。 |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: AscendC | 3 | title: AscendC |
| 4 | -description: 'AscendC 是昇腾原生 C++ 算子编程框架,允许显式调度 Cube、Vector、Scalar 及 L0/L1/L2/UB 内存,通过 Tiling、双 Buffer 和多核流水获得较高性能上限。DeepSeek-V4 的 SAS、Compressor、LI 同时覆盖 Atlas A3 与 950PR/DT:A3 使用四阶段 SAS,950 增加 FP8 反量化阶段并以 Histograms 加速 LI 精确 Top-k;S_k=16384 时理论约 7296 cycle,对比 A3 排序路径约 28375 cycle。模板 Cost Model 以搬运不慢于计算选择基本块,值依赖分核由 AICPU 生成 metadata;具体 metadata 算子实现在外部 ops-transformer。' | 4 | +description: AscendC 是昇腾原生 C++ 算子编程框架,提供 Cube、Vector、Scalar 调度、Tiling 和片上缓冲区管理;L2 缓存配置受接口、产品和版本限制。以 DeepSeek-V4 的 SAS、Compressor、LightningIndexer 说明 A3 与 950PR/DT 的实现差异,性能估算方法、对照口径与实测边界见正文。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.implementation | 6 | - task.implementation |
| 7 | - topic.architecture | 7 | - topic.architecture |
| @@ -13,18 +13,21 @@ sources: | |||
| 13 | role: primary | 13 | role: primary |
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | +- resource: https://gitcode.com/cann/asc-devkit/blob/e680c34c09ba6919b3cebd2b7ec027f4ff6f0f22/docs/zh/api/SIMD-API/basic_api/data_structures/GlobalTensor/SetL2CacheHint.md | ||
| 17 | + role: constraint | ||
| 18 | +- resource: https://gitcode.com/cann/asc-devkit/blob/e680c34c09ba6919b3cebd2b7ec027f4ff6f0f22/.agent/skills/ascendc-api-kernel-debug/references/clock_and_timestamp.md | ||
| 19 | + role: supporting | ||
| 16 | created_at: '2026-04-14T00:00:00Z' | 20 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 21 | +updated_at: '2026-10-28T06:00:57Z' |
| 18 | platforms: | 22 | platforms: |
| 19 | - a3 | 23 | - a3 |
| 20 | - '950' | 24 | - '950' |
| 21 | --- | 25 | --- |
| 22 | - | ||
| 23 | # AscendC | 26 | # AscendC |
| 24 | 27 | ||
| 25 | ## Summary | 28 | ## Summary |
| 26 | 29 | ||
| 27 | -华为昇腾原生的 C++ 算子编程框架,提供对 NPU 硬件的精细控制能力,用于实现高性能融合算子。在 cann-recipes-infer 中是三种算子框架中性能上限最高的选择。 | 30 | +华为昇腾原生的 C++ 算子编程框架,提供对 NPU 硬件的精细控制能力,用于实现高性能融合算子。具体后端选择需结合算子实现、图模式和目标平台验证,不能由框架名称判断性能排名。 |
| 28 | 31 | ||
| 29 | ## Architecture | 32 | ## Architecture |
| 30 | 33 | ||
| @@ -34,7 +37,7 @@ platforms: | |||
| 34 | 37 | ||
| 35 | ## Key Features | 38 | ## Key Features |
| 36 | 39 | ||
| 37 | -- 精细的内存管理(L0/L1/L2/UB 显式控制) | 40 | +- 按目标平台管理 L0/L1/UB 等片上缓冲区,组织数据搬运与复用;L2 缓存策略需单独核对接口支持范围 |
| 38 | - 多核并行调度 | 41 | - 多核并行调度 |
| 39 | - 指令级流水线优化 | 42 | - 指令级流水线优化 |
| 40 | - 支持自定义 Tiling 策略 | 43 | - 支持自定义 Tiling 策略 |
| @@ -49,6 +52,7 @@ platforms: | |||
| 49 | 52 | ||
| 50 | - 开发门槛较高,需理解 NPU 硬件架构 | 53 | - 开发门槛较高,需理解 NPU 硬件架构 |
| 51 | - 迭代速度不如 PyPTO 等高层框架 | 54 | - 迭代速度不如 PyPTO 等高层框架 |
| 55 | +- L2 缓存策略有平台和模式限制。[SetL2CacheHint](../../../../ops/ascendc/apis/simd_api/basic_api/data_structures/global_tensor/set_l2_cache_hint.md) 设置 GlobalTensor 是否启用 L2 Cache;固定版本文档列出 A2、A3、950PR/DT 支持,而 Atlas 200I/500 A2 推理产品不支持,并明确 `CACHE_MODE_PERSISTENT` 驻留模式尚不支持。不能把这一能力概括为全平台的 L2 显式分配或驻留保证;使用时还需核对目标 CANN 版本。 | ||
| 52 | 56 | ||
| 53 | ## DeepSeek-V4 框架级增强 | 57 | ## DeepSeek-V4 框架级增强 |
| 54 | 58 | ||
| @@ -56,9 +60,26 @@ DeepSeek-V4 0day 在 AscendC 上发布 `SparseAttnSharedKV`(SAS)、`Compressor` | |||
| 56 | 60 | ||
| 57 | ### 950PR/DT 直方图(Histograms)API 加速 Top-k | 61 | ### 950PR/DT 直方图(Histograms)API 加速 Top-k |
| 58 | 62 | ||
| 59 | -LI 的核心是在长达数十万的序列中为每 token **精确**选出分数最高的 Top-k(例 512,不能用近似)。950PR/DT 用 **Histograms 指令**:一个 cycle 把 64 个 uint8 数字分入 0-127 或 128-255 桶。方案三步:① **保序变换**(float→uint32,nan→0,负数取反,正数 ⊕2³¹);② **多级直方图分桶**(uint32 切 4×8 位逐级 `hist` 定位第 Top-k 值的每 8 位,uint16 只需二级);③ **向量化筛选**(按 `value_k` 比较聚集大于/等于项)。理论开销在 S_k=16384 时约 **7296 cycle**,显著优于 A3 上 sort32/mrgsort 的约 **28375 cycle**。详见 [lightning-indexer](../kernels/lightning_indexer.md)。 | 63 | +LI 的核心是在长达数十万的序列中为每 token **精确**选出分数最高的 Top-k(例 512,不能用近似)。950PR/DT 用 **Histograms 指令**:一个 cycle 把 64 个 uint8 数字分入 0-127 或 128-255 桶。方案三步:① **保序变换**(float→uint32,nan→0,负数取反,正数 ⊕2³¹);② **多级直方图分桶**(uint32 切 4×8 位逐级 `hist` 定位第 Top-k 值的每 8 位,uint16 只需二级);③ **向量化筛选**(按 `value_k` 比较聚集大于/等于项)。具体算法见 [lightning-indexer](../kernels/lightning_indexer.md),以下说明性能数字的获取方法和比较范围。 |
| 60 | 64 | ||
| 61 | -> Histograms 指令为 950PR/DT 平台能力,guide 以理论 cycle 推导描述其算法。 | 65 | +#### 理论周期如何得到、可以比较什么 |
| 66 | + | ||
| 67 | +数字来自 [固定版本 Guide 的「实现指令和理论开销」](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md#top-k-950prdt-方案)。估算对象为一个 Query 的 Top-k 阶段,Score 保序变换为 uint32,`S_k=16384`、`k=512`;不含 LI 的矩阵乘法、整网调度等其他阶段。 | ||
| 68 | + | ||
| 69 | +| 方案与平台 | 从来源取得估算值的方法 | 该例理论周期 | | ||
| 70 | +| --- | --- | --- | | ||
| 71 | +| 950PR/DT:直方图与向量化筛选 | 累加四级 hist/find 与 findGTO/findEQ 共十段的指令开销,得到 `111 × (S_k / 256) + 192` | `111 × 64 + 192 = 7296` cycle | | ||
| 72 | +| A3:sort32 与 mrgsort | 按同一 Guide 给出的排序/归并估算式计算,见下式 | 约 `28375` cycle | | ||
| 73 | + | ||
| 74 | +$$C_{A3} = \frac{1.36S_k + 3.32k + \left\lceil \frac{\max(\log_2(k)-5,0)}{2}S_k \right\rceil}{2}$$ | ||
| 75 | + | ||
| 76 | +代入同一组 `S_k`、`k`,A3 估算值为 `28375.04` cycle,两者的理论周期比约为 `3.89`。这是两个平台各自指令模型的对照,不能视为同平台替换算法后的实测加速比,也不能直接换算成整算子时延、TTFT 或 TPOT 收益。该 Guide 未给出这组数字对应的真机 Trace 或重复测量统计,本卡没有补造实测结果。 | ||
| 77 | + | ||
| 78 | +#### 如何补充实测与实现对照 | ||
| 79 | + | ||
| 80 | +1. 固定平台、CANN/编译版本、输入 Score 类型和分布、`S_k`、`k` 及 Query 数;先验证精确 Top-k 的结果正确,再比较性能。同平台有可运行的等价实现时作为基线,缺少对照时只报告观测值,不声称实现收益。 | ||
| 81 | +2. 若测 Top-k 局部周期,在同一线程的被测段前后使用 `clock()` 取差值;把打印移出测量区,区分预热与稳态,记录重复次数和分布,并说明同步等待、编译优化和调度影响。测量范围需与理论模型对齐;完整测量边界见 [Kernel 周期与时间戳测量](../../../../contrib/ascendc/runbooks/performance/kernel_clock_and_time_stamp_measurement.md)。 | ||
| 82 | +3. 同时记录基线周期、候选周期与理论估算,分别报告实测相对理论的偏差和 `1 - 候选周期 / 基线周期` 的周期下降比例。局部周期结果只支持局部结论;整算子耗时与整网指标应单独测量,不能沿用该比例。本段是复测方法,本次未执行设备测量。 | ||
| 62 | 83 | ||
| 63 | ### FP8 量化模式 | 84 | ### FP8 量化模式 |
| 64 | 85 | ||
| @@ -83,6 +104,8 @@ SAS / LI 这类「值依赖算子」仅靠 shape 无法做好分核,需运行 | |||
| 83 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — ops/ascendc/ 目录包含多个 AscendC 算子实现 | 104 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — ops/ascendc/ 目录包含多个 AscendC 算子实现 |
| 84 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md)(V4 AscendC:Compressor / LI 950PR/DT 直方图 / SAS / AICPU Scheduler,MR 985b882/5e9e41d) | 105 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md)(V4 AscendC:Compressor / LI 950PR/DT 直方图 / SAS / AICPU Scheduler,MR 985b882/5e9e41d) |
| 85 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/ascendc/src/{kv_compress_epilog, indexer_compress_epilog, hc_pre, hc_post}`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/ascendc/src) + [`torch_ops_extension/custom_ops/converter/`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/ascendc/torch_ops_extension/custom_ops/converter)(FP8 dtype / metadata 算子命名空间 grep 核对) | 106 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/ascendc/src/{kv_compress_epilog, indexer_compress_epilog, hc_pre, hc_post}`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/ascendc/src) + [`torch_ops_extension/custom_ops/converter/`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/ascendc/torch_ops_extension/custom_ops/converter)(FP8 dtype / metadata 算子命名空间 grep 核对) |
| 107 | +- [SetL2CacheHint 固定版本文档](https://gitcode.com/cann/asc-devkit/blob/e680c34c09ba6919b3cebd2b7ec027f4ff6f0f22/docs/zh/api/SIMD-API/basic_api/data_structures/GlobalTensor/SetL2CacheHint.md) — 产品支持情况与驻留模式边界;接口可用不代表所有缓存模式均可用。 | ||
| 108 | +- [clock 与时间戳固定版本说明](https://gitcode.com/cann/asc-devkit/blob/e680c34c09ba6919b3cebd2b7ec027f4ff6f0f22/.agent/skills/ascendc-api-kernel-debug/references/clock_and_timestamp.md) — 同线程局部周期差、打印位置及测量干扰;不将局部周期冒充完整算子或整网耗时。 | ||
| 86 | 109 | ||
| 87 | ## Known Kernels | 110 | ## Known Kernels |
| 88 | 111 | ||
| @@ -116,4 +139,4 @@ SAS / LI 这类「值依赖算子」仅靠 shape 无法做好分核,需运行 | |||
| 116 | - [precise-intersection](../../../spatial/concepts/kernels/precise_intersection.md)(跨域·空间智能:3DGS 高斯-像素块精确相交算子) | 139 | - [precise-intersection](../../../spatial/concepts/kernels/precise_intersection.md)(跨域·空间智能:3DGS 高斯-像素块精确相交算子) |
| 117 | - [gaussian-filter](../../../spatial/concepts/kernels/gaussian_filter.md)(跨域·空间智能:3DGS 视锥剔除融合算子) | 140 | - [gaussian-filter](../../../spatial/concepts/kernels/gaussian_filter.md)(跨域·空间智能:3DGS 视锥剔除融合算子) |
| 118 | - [hunyuan3d](../../../spatial/concepts/models/hunyuan3d.md)(跨域·空间智能:render_npu / 自研光栅化小算子的 AscendC 实现) | 141 | - [hunyuan3d](../../../spatial/concepts/models/hunyuan3d.md)(跨域·空间智能:render_npu / 自研光栅化小算子的 AscendC 实现) |
| 119 | -- [triton-on-ascend](../../../training/concepts/frameworks/triton_on_ascend.md)(与手写 AscendC 并列的第三条算子通道:Triton DSL 落昇腾) | 142 | +- [triton-on-ascend](../../../training/concepts/frameworks/triton_on_ascend.md)(与手写 AscendC 并列的第三条算子通道:Triton DSL 落昇腾) |
| @@ -1,7 +1,12 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: AutoFuse(Inductor NPU 自动融合编译组件) | 3 | title: AutoFuse(Inductor NPU 自动融合编译组件) |
| 4 | -description: 'AutoFuse 是面向昇腾 NPU 的 PyTorch Inductor JIT 自动融合组件,将 InductorLoopIR 降为 AscendLoopIR,经 Schedule 模板选择、UB 复用和 ATT 自动 Tiling 后生成 AscendC Kernel。调度以 ComputeType、AxisGroup 和 Merge 规则产生 TilingCase,求解器结合 LocalBuffer、核数、切分与流水耗时选择方案。它主要覆盖 Pointwise/Reduce 等可连续编译区域,避免断图和反复编译;当前 152 个 LoopIR 中覆盖 46 个。DeepSeek-V4 的 hc_post 片段在 Atlas A3 典型 shape 下由 30 us 降至 7.5 us(dynamic=False)或 10 us(dynamic=True),但端到端收益尚未验证。AutoFuse 与 GE/npugraph_ex 图后端是并列路径。' | 4 | +description: AutoFuse 是面向昇腾 NPU 的 PyTorch Inductor JIT 自动融合组件,将 InductorLoopIR 降为 |
| 5 | + AscendLoopIR,经 Schedule 模板选择、UB 复用和 ATT 自动 Tiling 后生成 AscendC Kernel。调度以 ComputeType、AxisGroup | ||
| 6 | + 和 Merge 规则产生 TilingCase,求解器结合 LocalBuffer、核数、切分与流水耗时选择方案。它主要覆盖 Pointwise/Reduce | ||
| 7 | + 等可连续编译区域,避免断图和反复编译;当前 152 个 LoopIR 中覆盖 46 个。DeepSeek-V4 的 hc_post 片段在 Atlas A3 典型 | ||
| 8 | + shape 下由 30 us 降至 7.5 us(dynamic=False)或 10 us(dynamic=True),但端到端收益尚未验证。AutoFuse | ||
| 9 | + 与 GE/npugraph_ex 图后端是并列路径。 | ||
| 5 | tags: | 10 | tags: |
| 6 | - task.implementation | 11 | - task.implementation |
| 7 | - topic.architecture | 12 | - topic.architecture |
| @@ -14,11 +19,10 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_autofuse_guide.md | 19 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_autofuse_guide.md |
| 15 | role: supporting | 20 | role: supporting |
| 16 | created_at: '2026-05-30T00:00:00Z' | 21 | created_at: '2026-05-30T00:00:00Z' |
| 17 | -updated_at: '2026-09-03T13:55:44Z' | 22 | +updated_at: '2026-10-22T10:35:31Z' |
| 18 | platforms: | 23 | platforms: |
| 19 | - a3 | 24 | - a3 |
| 20 | --- | 25 | --- |
| 21 | - | ||
| 22 | # AutoFuse(Inductor NPU 自动融合编译组件) | 26 | # AutoFuse(Inductor NPU 自动融合编译组件) |
| 23 | 27 | ||
| 24 | ## Summary | 28 | ## Summary |
| @@ -31,26 +35,7 @@ platforms: | |||
| 31 | 35 | ||
| 32 | ## 使能方式 | 36 | ## 使能方式 |
| 33 | 37 | ||
| 34 | -据 guide/README 文档说明的使能方式:PyTorch 前端增加一行导入即可注册基于 AutoFuse 的 Inductor NPU 后端扩展,随后对目标函数加 `@torch.compile` 装饰。(注:`import inductor_npu_ext` 仅出现在 guide/README 文档片段中;cann-recipes-infer 的 InductorAF 参考模型 `model.py` 仅含 `@torch.compile`,未含该后端扩展 import。) | 38 | +后端扩展负责接入 Inductor 编译链;具体导入、编译范围选择和验收由[部分编译规则](../../optimizations/rules/inductor_autofuse_partial_compile_scope.md)维护。固定模型中仅含装饰器不等于它自带后端注册。 |
| 35 | - | ||
| 36 | -```python | ||
| 37 | -import inductor_npu_ext # 注册基于 AscendC 的 inductor 编译后端扩展 | ||
| 38 | - | ||
| 39 | -@torch.compile | ||
| 40 | -def hc_post(x, residual, post, comb): | ||
| 41 | - y = post.unsqueeze(-1) * x.unsqueeze(-2) + torch.sum(comb.unsqueeze(-1) * residual.unsqueeze(-2), dim=2) | ||
| 42 | - return y.type_as(x) | ||
| 43 | -``` | ||
| 44 | - | ||
| 45 | -编译范围选择建议(与图模式的"避免断图/重编译"原则一致): | ||
| 46 | - | ||
| 47 | -- 编译范围内图结构稳定,不含执行时分支选择、`.item()` 内存同步等行为,避免过多 Graph Break。 | ||
| 48 | -- 避免编译范围过大——范围越大,dynamo guard 叠加越多,越易触发 re-compile。 | ||
| 49 | -- 优先选择含较多 Pointwise / Reduce 计算的范围,Inductor 对这类计算的融合效果好,编译代价小、收益高。 | ||
| 50 | - | ||
| 51 | -在 `models/deepseek-v4-flash-tilelang-and-inductorAF/` 参考模型中,加 `@torch.compile` 的不止 `hc_post`:实际编译范围包含 `Expert.forward`、`RMSNorm.forward`、`Transformer.hc_head`、`hc_pre`、`hc_post`(README §适配 inductor 编译的改动)。其中 `hc_pre` 用到的 TileLang 算子 `hc_split_sinkhorn` 被封装为 torch 自定义算子(`torch.library.Library("tl","FRAGMENT")` 注册,暴露为 `torch.ops.tl.hc_split_sinkhorn`),作为自定义函数与 `torch.compile` 配合以减少 dynamo 断图。 | ||
| 52 | - | ||
| 53 | -> AutoFuse 当前覆盖 152 个 LoopIR 中的 46 个(guide Highlights,未给完整清单),未来将逐步补齐至与 LoopIR 完整对等——这是当前完备度边界,覆盖范围会随版本变化;端到端多片段收益待验证。 | ||
| 54 | 39 | ||
| 55 | ## JIT 算子生成的三阶段 | 40 | ## JIT 算子生成的三阶段 |
| 56 | 41 | ||
| @@ -106,7 +91,7 @@ Cost(MTE2) = ((DataSize / T + h) * Count) + H | |||
| 106 | 91 | ||
| 107 | 其中 `带宽 T` 是与向量化宽度对齐、分核数、GM/UB 非连续性相关的因变量。计算类 API 对**稳定基础 API** 采集性能数据建模,对**易变 API** 基于调用逻辑计算入参/调用次数生成完整模型。 | 92 | 其中 `带宽 T` 是与向量化宽度对齐、分核数、GM/UB 非连续性相关的因变量。计算类 API 对**稳定基础 API** 采集性能数据建模,对**易变 API** 基于调用逻辑计算入参/调用次数生成完整模型。 |
| 108 | 93 | ||
| 109 | -**轴排序求解器**先定切分轴优先级(父轴>子轴是功能性硬约束;规约化轴>非规约化轴、广播轴>非广播轴、非最内轴>最内轴、搬运 API 尾轴同优先级,均为启发式规则),再分**核内 Tiling**(逆序遍历,优先把变量调至最大值并判断硬件约束,不满足则二分调整)与**分核 Tiling**(识别多核相关变量,按从大到小遍历,优先选占用核数更大的记录,超物理核数则返回)。另有 `UB 占用核数占用权衡算法`(精细控核+UB)与 `对称切分`(尾轴转置场景,多尾轴同优先级切分)。 | 94 | +**轴排序求解器**按策略确定轴优先级;这里的优先级表示优先不切该轴。固定来源以适合 API 头开销较大场景的 **UB 优先贪心算法**为例:父轴优先于子轴是功能性约束,规约化轴、广播轴、非最内轴等排序属于该策略的启发式。该示例的核内 Tiling 逆序遍历变量,在硬件约束内调大取值,不满足时二分调整;分核 Tiling 再遍历多核变量,优先选择核数较大的可行记录。这不是所有轴排序算法的共同流程;来源另列 UB/核数权衡和对称切分策略。 |
| 110 | 95 | ||
| 111 | ### Codegen:impl_graph → AscendC kernel | 96 | ### Codegen:impl_graph → AscendC kernel |
| 112 | 97 | ||
| @@ -125,13 +110,7 @@ AscendLoopIR 是一套基于 Loop 定义的 IR,其 type 映射到明确的 vec | |||
| 125 | 110 | ||
| 126 | ## 典型片段收益 | 111 | ## 典型片段收益 |
| 127 | 112 | ||
| 128 | -DeepSeek-V4 的 `hc_post` 片段含多个小算子组合,适合 AutoFuse 融合。对 `hc_post` 加 `@torch.compile` 后的实测(昇腾 A3、基于 `hc_post` 典型输入 shape:`x=(1,1,4096)` bf16 / `residual=(1,1,4,4096)` bf16 / `post=(1,1,4)` fp32 / `comb=(1,1,4,4)` fp32,README §基准脚本): | 113 | +[A3 部分编译和 hc_post 来源观测](../../optimizations/benchmark_observations/autofuse_a3_source_benchmark.md)维护输入、baseline、计时和限制。 |
| 129 | - | ||
| 130 | -| 模型部分 | 未编译耗时 | 编译后(dynamic=False) | 提升 | 编译后(dynamic=True) | 提升 | | ||
| 131 | -| --- | --- | --- | --- | --- | --- | | ||
| 132 | -| hc_post | 30 us | 7.5 us | 4x | 10 us | 3x | | ||
| 133 | - | ||
| 134 | -> 该数据前提为昇腾 A3 平台、`hc_post` 单算子上述典型输入 shape;端到端多片段收益待验证。cann-recipes-infer 的 `hc_post` 已有 AscendC 手写融合算子实现(见 [mhc-hcpre-hcpost](../kernels/mhc_hcpre_hcpost.md));此处 AutoFuse 是**自动融合**路径,与手写 AscendC 算子为并列两种实现来路。 | ||
| 135 | 114 | ||
| 136 | ## Evidence | 115 | ## Evidence |
| 137 | 116 | ||
| @@ -149,4 +128,4 @@ DeepSeek-V4 的 `hc_post` 片段含多个小算子组合,适合 AutoFuse 融 | |||
| 149 | - [deepseek-v4](../models/deepseek_v4.md)(AutoFuse 在 V4 上的应用模型) | 128 | - [deepseek-v4](../models/deepseek_v4.md)(AutoFuse 在 V4 上的应用模型) |
| 150 | - [torchtitan](../../../training/concepts/frameworks/torchtitan.md)(跨域·训练:TorchTitan 训练入图 Future 对接 AutoFuse) | 129 | - [torchtitan](../../../training/concepts/frameworks/torchtitan.md)(跨域·训练:TorchTitan 训练入图 Future 对接 AutoFuse) |
| 151 | - [deepseek-v32-pretrain](../../../training/guides/recipes/deepseek_v32_pretrain.md)(跨域·训练:DeepSeek-V3.2 预训练(Bypass Triton → 未来 AutoFuse)) | 130 | - [deepseek-v32-pretrain](../../../training/guides/recipes/deepseek_v32_pretrain.md)(跨域·训练:DeepSeek-V3.2 预训练(Bypass Triton → 未来 AutoFuse)) |
| 152 | -- [autofuse-train](../../../training/concepts/techniques/autofuse_train.md)(跨域·训练:同一 CANN AutoFuse 组件在训练侧(DeepSeek-V4)的入图应用与收益) | 131 | +- [autofuse-train](../../../training/concepts/techniques/autofuse_train.md)(跨域·训练:同一 CANN AutoFuse 组件在训练侧(DeepSeek-V4)的入图应用与收益) |
| @@ -1,12 +1,13 @@ | |||
| 1 | # 框架 | 1 | # 框架 |
| 2 | 2 | ||
| 3 | -- [AscendC](ascendc.md):AscendC 是昇腾原生 C++ 算子编程框架,允许显式调度 Cube、Vector、Scalar 及 L0/L1/L2/UB 内存,通过 Tiling、双 Buffer 和多核流水获得较高性能上限。DeepSeek-V4 的 SAS、Compressor、LI 同时覆盖 Atlas A3 与 950PR/DT:A3 使用四阶段 SAS,950 增加 FP8 反量化阶段并以 Histograms 加速 LI 精确 Top-k;S_k=16384 时理论约 7296 cycle,对比 A3 排序路径约 28375 cycle。模板 Cost Model 以搬运不慢于计算选择基本块,值依赖分核由 AICPU 生成 metadata;具体 metadata 算子实现在外部 ops-transformer。 | 3 | +- [AscendC 算子编程机制](ascendc.md):AscendC 的计算调度与片上缓冲区管理,以及受产品和版本限制的 L2 缓存配置;以 DeepSeek-V4 说明 A3 与 950PR/DT 的实现差异,提供 Top-k 理论周期推导、对照口径和实测方法。 |
| 4 | - [AutoFuse(Inductor NPU 自动融合编译组件)](autofuse.md):AutoFuse 是面向昇腾 NPU 的 PyTorch Inductor JIT 自动融合组件,将 InductorLoopIR 降为 AscendLoopIR,经 Schedule 模板选择、UB 复用和 ATT 自动 Tiling 后生成 AscendC Kernel。调度以 ComputeType、AxisGroup 和 Merge 规则产生 TilingCase,求解器结合 LocalBuffer、核数、切分与流水耗时选择方案。它主要覆盖 Pointwise/Reduce 等可连续编译区域,避免断图和反复编译;当前 152 个 LoopIR 中覆盖 46 个。DeepSeek-V4 的 hc_post 片段在 Atlas A3 典型 shape 下由 30 us 降至 7.5 us(dynamic=False)或 10 us(dynamic=True),但端到端收益尚未验证。AutoFuse 与 GE/npugraph_ex 图后端是并列路径。 | 4 | - [AutoFuse(Inductor NPU 自动融合编译组件)](autofuse.md):AutoFuse 是面向昇腾 NPU 的 PyTorch Inductor JIT 自动融合组件,将 InductorLoopIR 降为 AscendLoopIR,经 Schedule 模板选择、UB 复用和 ATT 自动 Tiling 后生成 AscendC Kernel。调度以 ComputeType、AxisGroup 和 Merge 规则产生 TilingCase,求解器结合 LocalBuffer、核数、切分与流水耗时选择方案。它主要覆盖 Pointwise/Reduce 等可连续编译区域,避免断图和反复编译;当前 152 个 LoopIR 中覆盖 46 个。DeepSeek-V4 的 hc_post 片段在 Atlas A3 典型 shape 下由 30 us 降至 7.5 us(dynamic=False)或 10 us(dynamic=True),但端到端收益尚未验证。AutoFuse 与 GE/npugraph_ex 图后端是并列路径。 |
| 5 | - [CANN Executor(cann-recipes-infer 自研 executor)](cann_executor.md):CANN Executor 是 cann-recipes-infer 自带、无需 vLLM/SGLang 的轻量推理执行框架;本页聚焦新执行器路径。它以 OfflineInference/OnlineInference→Scheduler→ExecutionEngine→ModelWorker→Model 五层组织离线批处理与在线 PD 分离,统一由 InferenceConfig 配置,并支持 eager、ge_graph、npugraph_ex、Packed Sequence、Paged KV Cache、Profiler 与通信域管理。图模式仅覆盖 Decode,Prefill 走 eager。模型需实现 forward、init_cache、load_weights,并通过 CommManager 注册 attention、FFN、MoE、embedding、lm_head、MC2 等计算组;OnlineInference 另建 Gloo 调度组。仓内还保留 legacy ModelRunner 和 HSTU 独立路径,它们不受本页接口约束。 | 5 | - [CANN Executor(cann-recipes-infer 自研 executor)](cann_executor.md):CANN Executor 是 cann-recipes-infer 自带、无需 vLLM/SGLang 的轻量推理执行框架;本页聚焦新执行器路径。它以 OfflineInference/OnlineInference→Scheduler→ExecutionEngine→ModelWorker→Model 五层组织离线批处理与在线 PD 分离,统一由 InferenceConfig 配置,并支持 eager、ge_graph、npugraph_ex、Packed Sequence、Paged KV Cache、Profiler 与通信域管理。图模式仅覆盖 Decode,Prefill 走 eager。模型需实现 forward、init_cache、load_weights,并通过 CommManager 注册 attention、FFN、MoE、embedding、lm_head、MC2 等计算组;OnlineInference 另建 Gloo 调度组。仓内还保留 legacy ModelRunner 和 HSTU 独立路径,它们不受本页接口约束。 |
| 6 | - [KV Cache Manager(框架级 Paged Attention KV Cache 管理)](kv_cache_manager.md):KV Cache Manager 是 CANN Executor 的 Paged Attention 缓存层,采用 KVCacheManager→SingleTypeKVCacheManager→BlockPool 三层结构协调请求、逻辑块与物理块。allocate_slots 先全局预检再统一分配,避免部分成功;SlidingWindow 使用固定块数,FullAttention 随序列增长,离线按 max_len、在线按显存预算计算,并以 _null_block 保证 block table 索引合法,MTP 额外预留 2×next_n 缓存。缓存按 manager_key 分组,同一注意力类型可因 block_size 不同拆成多个 manager;block_table 与 slot_mapping 也按该键生成。模型未返回 ModelCacheInfo 时回退到模型自管缓存。 | 6 | - [KV Cache Manager(框架级 Paged Attention KV Cache 管理)](kv_cache_manager.md):KV Cache Manager 是 CANN Executor 的 Paged Attention 缓存层,采用 KVCacheManager→SingleTypeKVCacheManager→BlockPool 三层结构协调请求、逻辑块与物理块。allocate_slots 先全局预检再统一分配,避免部分成功;SlidingWindow 使用固定块数,FullAttention 随序列增长,离线按 max_len、在线按显存预算计算,并以 _null_block 保证 block table 索引合法,MTP 额外预留 2×next_n 缓存。缓存按 manager_key 分组,同一注意力类型可因 block_size 不同拆成多个 manager;block_table 与 slot_mapping 也按该键生成。模型未返回 ModelCacheInfo 时回退到模型自管缓存。 |
| 7 | - [MM Inference Launch(多模态生成模型统一推理拉起架构)](mm_inference_launch.md):MM Inference Launch 是 cann-recipes-infer 为 Wan2.2-I2V、HunyuanVideo、SANA-Video 和 HunyuanImage-3.0 提供的统一多模态推理启动层。各模型以 infer.sh 和场景 YAML 为入口,mm_function.sh 负责字段校验、环境合并、cache/sparse 参数生成,并选择 torchrun 或 accelerate,日志统一写入模型 res 目录。YAML 对必填键、未知键、端口/卡数类型以及 Dit-Cache 和 sparse 方法枚举做校验;环境优先级为 NPU 默认→YAML→MODEL_BASE 派生→HCCL→用户 export。HunyuanVideo 的块稀疏 Attention 可配单卡或 Ulysses,但不支持 Ring,且与 cache 互斥。该体系独立于 LLM 的 CANN Executor。 | 7 | - [MM Inference Launch(多模态生成模型统一推理拉起架构)](mm_inference_launch.md):MM Inference Launch 是 cann-recipes-infer 为 Wan2.2-I2V、HunyuanVideo、SANA-Video 和 HunyuanImage-3.0 提供的统一多模态推理启动层。各模型以 infer.sh 和场景 YAML 为入口,mm_function.sh 负责字段校验、环境合并、cache/sparse 参数生成,并选择 torchrun 或 accelerate,日志统一写入模型 res 目录。YAML 对必填键、未知键、端口/卡数类型以及 Dit-Cache 和 sparse 方法枚举做校验;环境优先级为 NPU 默认→YAML→MODEL_BASE 派生→HCCL→用户 export。HunyuanVideo 的块稀疏 Attention 可配单卡或 Ulysses,但不支持 Ring,且与 cache 互斥。该体系独立于 LLM 的 CANN Executor。 |
| 8 | -- [在线 PD 分离推理执行机制(Online PD-Disaggregated Inference)](online_pd_inference.md):Online PD Inference 是 CANN Executor 在离线执行器之上的 Prefill/Decode 分离服务模式:Prefill 通常采用 TP,Decode 通常采用 DP,Prefill 完成后通过 KVTransferManager 将 KV 直接传至 Decode。每卡一 worker,leader 启动 FastAPI 与 DPDispatcher;Bootstrap 管理 rank/room 拓扑,Router 按 room 哈希并发请求两侧,数据面使用 ZMQ 控制与 RDMA 写入 64B metadata 槽。Prefill/Decode 各有独立 Scheduler;Decode 入场时按 num_reserved_decode_tokens(默认 64)预留块,仅在扣除流水保留量后仍足够才接纳,以避免死锁,并支持 MTP 预分配和 OOM 驱逐。在线模式只有 PREFILL/DECODE,NONE 表示离线;HTTP 端口目前固定。 | 8 | +- [PyPTO机制](pypto.md):PyPTO 以声明式 Tensor 图表达融合算子,经 PTO-ISA、MPMD 调度和后端编译实现不同硬件的适配;本卡维护框架机制,Duration 与 VF 收益由独立观测记录。 |
| 9 | -- [PyPTO](pypto.md):PyPTO 是以声明式 Python Tensor 图编写融合 kernel 的自研框架,面向 MegaKernel 级融合,在 A2/A3/950PR/950DT 间通过 PTO-ISA 与编译 Pass 适配代际差异。它用 MPMD 自动并行无依赖的 Cube/Vector 任务,以 loop_unroll 的候选档位把动态 shape 转为静态子图,并提供 split-K、buffer 合图、combine_axis 免广播及 scatter_update_3d 等优化。DeepSeek-V4 的 hc_pre、mla_prolog、compressor 等算子均有 PyPTO 实现;hc_post 的 Vector Fusion 报告提升 22.7%,但完整 shape 和端到端收益仍待验证。PTO ISA、编译器和 DFX 的完整实现位于外部 Cann-PyPTO 仓。 | ||
| 10 | - [SGLang](sglang.md):开源 LLM 推理服务框架,在 cann-recipes-infer 中作为多个模型的基础推理引擎,提供原生的并行能力(TP、EP、DP)和在线推理服务能力。 | 9 | - [SGLang](sglang.md):开源 LLM 推理服务框架,在 cann-recipes-infer 中作为多个模型的基础推理引擎,提供原生的并行能力(TP、EP、DP)和在线推理服务能力。 |
| 11 | - [TileLang](tilelang.md):TileLang 是基于 TVM、面向 GPU/NPU/CPU 的 Tile 级算子语言,以类 Python 语法分离数据流与线程绑定、布局、张量化和流水调度。CANN 对接路径经多级 Lowering 生成 Ascend 代码、毕昇编译为 .so,再由 torch_npu 加载;已用于 DSA 的 Lightning Indexer、Sparse Flash Attention。DeepSeek-V4 Developer 模式提供 AscendC 与 PTO-AS 两个后端,通过自动 CV/核内同步、32B 对齐的生命周期内存复用及 CV 指令拆分,将样例代码量降至传统后端约 20%。当前 V4 样例覆盖 Sparse Flash Attention、mHC Sinkhorn、Int8 GEMM 和激活量化;T.Pipelined 是文档能力,但在该批样例中未发现调用。 | 10 | - [TileLang](tilelang.md):TileLang 是基于 TVM、面向 GPU/NPU/CPU 的 Tile 级算子语言,以类 Python 语法分离数据流与线程绑定、布局、张量化和流水调度。CANN 对接路径经多级 Lowering 生成 Ascend 代码、毕昇编译为 .so,再由 torch_npu 加载;已用于 DSA 的 Lightning Indexer、Sparse Flash Attention。DeepSeek-V4 Developer 模式提供 AscendC 与 PTO-AS 两个后端,通过自动 CV/核内同步、32B 对齐的生命周期内存复用及 CV 指令拆分,将样例代码量降至传统后端约 20%。当前 V4 样例覆盖 Sparse Flash Attention、mHC Sinkhorn、Int8 GEMM 和激活量化;T.Pipelined 是文档能力,但在该批样例中未发现调用。 |
| 12 | -- [TorchAir](torchair.md):**Torch Ascend Intermediate Representation**,是 Ascend Extension for PyTorch(torch_npu)的图模式能力扩展库,提供昇腾设备亲和的 `torch.compile` 后端,实现 PyTorch 网络在 NPU 上的图模式推理加速与性能优化。 DeepSeek-R1 Decode 阶段走 TorchAir 图模式以规避 eager 下的 host 下发 bound,是 W8A8C8 + MTP1 benchmark 达到 TPOT 44.9 ms 的基础条件之一。 | 11 | +- [TorchAir 图编译机制](torchair.md):TorchAir 提供昇腾 torch.compile 后端;解释 GE 与图重放的机制及版本命名差异,模型部署和收益分别查阅对应入口。 |
| 12 | + | ||
| 13 | +- [在线 PD 分离推理执行机制:多机实例、DP 分发与 KV 传输(Online PD Inference)](online_pd_inference.md):CANN Executor 的在线 PD 分离机制:Prefill 与 Decode 实例可跨机,各自配置并行拓扑,通过 Bootstrap 发现 Rank,DPDispatcher 分发请求,KVTransferManager 传送 KV。说明 Router、调度与入场控制;不由该机制推定任意 DP+EP 组合均已验证。 | ||
| @@ -1,7 +1,10 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | -title: 在线 PD 分离推理执行机制(Online PD-Disaggregated Inference) | 3 | +title: 在线 PD 分离推理执行机制:多机实例、DP 分发与 KV 传输(Online PD Inference) |
| 4 | -description: 'Online PD Inference 是 CANN Executor 在离线执行器之上的 Prefill/Decode 分离服务模式:Prefill 通常采用 TP,Decode 通常采用 DP,Prefill 完成后通过 KVTransferManager 将 KV 直接传至 Decode。每卡一 worker,leader 启动 FastAPI 与 DPDispatcher;Bootstrap 管理 rank/room 拓扑,Router 按 room 哈希并发请求两侧,数据面使用 ZMQ 控制与 RDMA 写入 64B metadata 槽。Prefill/Decode 各有独立 Scheduler;Decode 入场时按 num_reserved_decode_tokens(默认 64)预留块,仅在扣除流水保留量后仍足够才接纳,以避免死锁,并支持 MTP 预分配和 OOM 驱逐。在线模式只有 PREFILL/DECODE,NONE 表示离线;HTTP 端口目前固定。' | 4 | +description: CANN Executor 的在线 PD 分离机制:Prefill 与 Decode 实例可跨机,各自配置并行拓扑,通过 Bootstrap 发现 Rank,DPDispatcher 分发请求,KVTransferManager 传送 KV。说明 Router、调度与入场控制;不由该机制推定任意 DP+EP 组合均已验证。 |
| 5 | +aliases: | ||
| 6 | +- Online PD Inference | ||
| 7 | +- Online PD-Disaggregated Inference | ||
| 5 | tags: | 8 | tags: |
| 6 | - task.implementation | 9 | - task.implementation |
| 7 | - topic.architecture | 10 | - topic.architecture |
| @@ -14,11 +17,21 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/design/online_inference_design.md | 17 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/design/online_inference_design.md |
| 15 | role: supporting | 18 | role: supporting |
| 16 | created_at: '2026-05-30T00:00:00Z' | 19 | created_at: '2026-05-30T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 20 | +updated_at: '2026-10-23T09:41:54Z' |
| 18 | platforms: | 21 | platforms: |
| 19 | - agnostic | 22 | - agnostic |
| 23 | +scope: | ||
| 24 | + schema: model.inference.v1 | ||
| 25 | + any_of: | ||
| 26 | + - all_of: | ||
| 27 | + serving_mode: | ||
| 28 | + - online | ||
| 29 | + deployment_topology: | ||
| 30 | + all_of: | ||
| 31 | + - pd_disaggregated | ||
| 32 | + framework: | ||
| 33 | + - cann_executor | ||
| 20 | --- | 34 | --- |
| 21 | - | ||
| 22 | # 在线 PD 分离推理执行机制(Online PD-Disaggregated Inference) | 35 | # 在线 PD 分离推理执行机制(Online PD-Disaggregated Inference) |
| 23 | 36 | ||
| 24 | ## Summary | 37 | ## Summary |
| @@ -69,7 +82,7 @@ flowchart TD | |||
| 69 | | `PUT` | `/bootstrap/register_rank` | Prefill rank | 启动时注册 `rank_ip / rank_port` 与拓扑参数 | | 82 | | `PUT` | `/bootstrap/register_rank` | Prefill rank | 启动时注册 `rank_ip / rank_port` 与拓扑参数 | |
| 70 | | `GET` | `/bootstrap/route` | Decode rank | 拉整份 rank_table + 集群视图 | | 83 | | `GET` | `/bootstrap/route` | Decode rank | 拉整份 rank_table + 集群视图 | |
| 71 | | `POST` | `/bootstrap/register_dp_rank` | Prefill(per-request) | 写 `bootstrap_room → dp_rank` | | 84 | | `POST` | `/bootstrap/register_dp_rank` | Prefill(per-request) | 写 `bootstrap_room → dp_rank` | |
| 72 | -| `POST` | `/bootstrap/query_dp_ranks` | Decode(per-request) | 批量查 `room → dp_rank`;跳过条件挂在字段值而非 `dp_size`:`_resolve_prefill_dp_rank`(`queues.py:93`)只要 `disagg_prefill_dp_rank is not None and >= 0` 即早返回该值、不进 `dp_size` 分支。Router 对**任意** `dp_size` 都 `setdefault 0`(`router.py:127`),故 Router 转发的请求恒跳过查询。`dp_size==1` 时 0 才是真实落点;`dp_size>1` 时 0 是占位,decode 仍据此 init 而**不**回查(详见 Router 节) | | 85 | +| `POST` | `/bootstrap/query_dp_ranks` | Decode(per-request) | 批量查 `room → dp_rank`;跳过条件挂在字段值而非 `dp_size`:`_resolve_prefill_dp_rank`(`queues.py:93`)只要 `disagg_prefill_dp_rank is not None and >= 0` 即早返回该值、不进 `dp_size` 分支。Router 仅在请求缺字段时 `setdefault 0`(`router.py:127`),此时跳过查询;显式 None/负数且 Prefill 多 DP 时仍查询。默认 0 在多 DP 下不保证是真实落点(详见 Router 节) | |
| 73 | | `GET` | `/bootstrap/health` | 运维 | 只读健康 | | 86 | | `GET` | `/bootstrap/health` | 运维 | 只读健康 | |
| 74 | 87 | ||
| 75 | 首请求允入条件:**Prefill** 需本实例 `is_ready == True`;**Decode** 需能 `GET /bootstrap/route` 拿到 prefill 拓扑(请求级触发,非启动期);**Router** 无 ready 概念,可先于 PD 启动,目标后端不在线时返回 503。 | 88 | 首请求允入条件:**Prefill** 需本实例 `is_ready == True`;**Decode** 需能 `GET /bootstrap/route` 拿到 prefill 拓扑(请求级触发,非启动期);**Router** 无 ready 概念,可先于 PD 启动,目标后端不在线时返回 503。 |
| @@ -78,7 +91,7 @@ flowchart TD | |||
| 78 | 91 | ||
| 79 | `router.py`。监听 `ROUTER_HTTP_PORT`,处理客户端请求时:① 注入 `bootstrap_room / bootstrap_host / bootstrap_port` 三元组(无 `bootstrap_room` 时自动生成:`uuid.uuid4().int & ((1<<63)-1)`,即 uuid4 取低 63 位,`router.py:114`);② 按 `bootstrap_room % N` 选一个 Prefill 实例与一个 Decode 实例,`asyncio.gather` **并发双发** POST 给两端 leader;③ 透传 Decode 响应给 client。 | 92 | `router.py`。监听 `ROUTER_HTTP_PORT`,处理客户端请求时:① 注入 `bootstrap_room / bootstrap_host / bootstrap_port` 三元组(无 `bootstrap_room` 时自动生成:`uuid.uuid4().int & ((1<<63)-1)`,即 uuid4 取低 63 位,`router.py:114`);② 按 `bootstrap_room % N` 选一个 Prefill 实例与一个 Decode 实例,`asyncio.gather` **并发双发** POST 给两端 leader;③ 透传 Decode 响应给 client。 |
| 80 | 93 | ||
| 81 | -**`disagg_prefill_dp_rank` 注入的真实语义**(`router.py:122-127`):Router 对该字段做 `payload.setdefault("disagg_prefill_dp_rank", 0)`——**无条件硬编码 0**,并非按 `dp_size` 预计算的真实 dp_rank。Router 既已对**任意** `dp_size` 都注入 0,decode 侧 `DecodePreallocQueue._resolve_prefill_dp_rank`(`queues.py:92-100`)首判 `if disagg_prefill_dp_rank is not None and >= 0: return it`——注入的 0 直接短路、早返回,decode 即以该 0 `receiver.init(0)`,**不**进入 `query_prefill_dp_ranks`(该查询仅在 `_resolve_prefill_dp_rank` 返回 None 时由 `need_query` 触发,`queues.py:154-164`)。因此 `query_dp_ranks` 回读真实 dp_rank 的路径**对 Router 转发的请求是死路径、不执行**——仅当请求不经 Router 或显式不带 `disagg_prefill_dp_rank` 字段(None/<0)时才回退查询。`dp_size==1` 时 0 恰好是正确值,decode 据此 `init()` 即对;`dp_size>1` 时 0 仅为占位、不代表真实落点:真实落点由 leader 节点 `DPDispatcher._get_next_dp_rank()`(`dp_dispatcher.py:144-151`)round-robin 选 DP leader,sender 侧 `AscendKVSender._register_prefill_dp_rank`(`conn.py:62-78`,仅 `is_dp_leader` rank)把**真实** dp_rank 经 `POST /bootstrap/register_dp_rank` 写入 bootstrap `room_to_dp_rank`——但 decode 当前**并不回读**该注册表(被注入的 0 短路),sender 的注册对 Router 转发链路是写而不被读。> ⚠️ doc≠code:`router.py:122-127` 注释声称 `dp_size>1` 时"leave the field unset、decode falls back to querying",但 `setdefault` 实际仍写入 0;`dp_size>1` 下 sender 虽把真实 dp_rank 注册进 bootstrap,但 decode 因 0 短路并不回查,注释描述的 fallback 路径在 Router 当前实现下不触达。 | 94 | +**`disagg_prefill_dp_rank` 的默认注入**(`router.py:122-127`):`payload.setdefault("disagg_prefill_dp_rank", 0)` 只在客户端未提供字段时写入 0,已提供的值原样保留。Decode 的 `_resolve_prefill_dp_rank` 对非负值直接返回,因此缺字段而被补 0 的请求跳过 bootstrap 查询;显式传入 `None` 或负数时,Prefill DP 大于 1 的请求仍可进入 `query_prefill_dp_ranks`。单 DP 下 0 是正确落点;多 DP 下真实落点由 `DPDispatcher` 轮转选择并由 sender 注册到 bootstrap,默认 0 不保证代表该落点。来源注释称多 DP 时应留空触发查询,而实际默认注入 0;这一差异仅适用于缺字段的请求,不能称所有 Router 请求都不回查。 |
| 82 | 95 | ||
| 83 | **双发语义**:prefill 的 HTTP 响应在 Router 内被丢弃,仅用于触发 prefill 处理 + 探测后端是否在线;以 decode 响应作为返回 body。错误传播:prefill HTTP 错误 → prefill rank 自身记日志、不影响 decode 主响应;decode 非 200 → 以 decode 的 status/body 返回 client;任一连接异常(`httpx.TransportError`)→ Router 抛 503 `backend unavailable`。这使请求级失败(控制面)与传输级失败(数据面)可独立观察处理。 | 96 | **双发语义**:prefill 的 HTTP 响应在 Router 内被丢弃,仅用于触发 prefill 处理 + 探测后端是否在线;以 decode 响应作为返回 body。错误传播:prefill HTTP 错误 → prefill rank 自身记日志、不影响 decode 主响应;decode 非 200 → 以 decode 的 status/body 返回 client;任一连接异常(`httpx.TransportError`)→ Router 抛 503 `backend unavailable`。这使请求级失败(控制面)与传输级失败(数据面)可独立观察处理。 |
| 84 | 97 | ||
| @@ -127,9 +140,9 @@ PD 模式下基类 `Scheduler` 被两个角色专一子类替换,二者继承 | |||
| 127 | 140 | ||
| 128 | **HOL-blocking 规避**:入场遍历对单个不可装请求用 `continue` 跳过而非 `break` 中断遍历,使队首大请求(块预算或 `allocate_slots` 失败)不阻断其后小请求入场(规避 head-of-line blocking)。 | 141 | **HOL-blocking 规避**:入场遍历对单个不可装请求用 `continue` 跳过而非 `break` 中断遍历,使队首大请求(块预算或 `allocate_slots` 失败)不阻断其后小请求入场(规避 head-of-line blocking)。 |
| 129 | 142 | ||
| 130 | -## 配置与启动 | 143 | +## 配置与传输接口 |
| 131 | 144 | ||
| 132 | -`SchedulerConfig.num_reserved_decode_tokens` 默认 **64**(`inference_config.py`),即上文入场控制的 per-pipeline 单请求 KV 预留量;高 QPS 突发场景增大可减少 retraction 频率、但牺牲并发吞吐。 | 145 | +`SchedulerConfig.num_reserved_decode_tokens` 默认 **64**(`inference_config.py`),即 per-pipeline 单请求 KV 预留量;入场控制、调整依据和验证见[KV 入场控制规则](../../optimizations/rules/online_decode_kv_admission_retraction.md)。 |
| 133 | 146 | ||
| 134 | 端口常量(`executor/online/constants.py`,**四个 HTTP 端口不可配**,调整需改源码): | 147 | 端口常量(`executor/online/constants.py`,**四个 HTTP 端口不可配**,调整需改源码): |
| 135 | 148 | ||
| @@ -174,4 +187,4 @@ ZMQ_PULL_PORT = 5556 # decode 经 zmq_ports_for_role() 统一 +2 偏移, | |||
| 174 | 187 | ||
| 175 | - **doc≠code**:`online_inference_design.md §7.6` 的 `DisaggConfig` 代码片段未标注该类的定义文件、且漏列 `engine_backend` 字段;实际定义在 `executor/core/config/inference_config.py`(`class DisaggConfig`,含 `engine_backend: Literal["memfabric","mooncake"] = "memfabric"`),默认后端为 memfabric(`AscendTransferEngine` 基于 `memfabric_hybrid`),mooncake 为 opt-in。 | 188 | - **doc≠code**:`online_inference_design.md §7.6` 的 `DisaggConfig` 代码片段未标注该类的定义文件、且漏列 `engine_backend` 字段;实际定义在 `executor/core/config/inference_config.py`(`class DisaggConfig`,含 `engine_backend: Literal["memfabric","mooncake"] = "memfabric"`),默认后端为 memfabric(`AscendTransferEngine` 基于 `memfabric_hybrid`),mooncake 为 opt-in。 |
| 176 | - **gap**:`MooncakeAscendTransferEngine` / HIXL 的具体内存对齐、session 握手等底层细节不在本设计文档范围;此处仅述后端分派入口与 2 MiB 对齐约束,不展开。 | 189 | - **gap**:`MooncakeAscendTransferEngine` / HIXL 的具体内存对齐、session 握手等底层细节不在本设计文档范围;此处仅述后端分派入口与 2 MiB 对齐约束,不展开。 |
| 177 | -- **gap**:源未给出 Router 的负载均衡是否超出 `bootstrap_room % N` 哈希(如按实例负载动态选择);当前仅哈希取模。 | 190 | +- **gap**:源未给出 Router 的负载均衡是否超出 `bootstrap_room % N` 哈希(如按实例负载动态选择);当前仅哈希取模。 |
| @@ -1,7 +1,8 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: PyPTO | 3 | title: PyPTO |
| 4 | -description: 'PyPTO 是以声明式 Python Tensor 图编写融合 kernel 的自研框架,面向 MegaKernel 级融合,在 A2/A3/950PR/950DT 间通过 PTO-ISA 与编译 Pass 适配代际差异。它用 MPMD 自动并行无依赖的 Cube/Vector 任务,以 loop_unroll 的候选档位把动态 shape 转为静态子图,并提供 split-K、buffer 合图、combine_axis 免广播及 scatter_update_3d 等优化。DeepSeek-V4 的 hc_pre、mla_prolog、compressor 等算子均有 PyPTO 实现;hc_post 的 Vector Fusion 报告提升 22.7%,但完整 shape 和端到端收益仍待验证。PTO ISA、编译器和 DFX 的完整实现位于外部 Cann-PyPTO 仓。' | 4 | +description: PyPTO 以声明式 Tensor 图表达融合算子,经 PTO-ISA、MPMD 调度和后端编译实现不同硬件的适配;本卡维护框架机制,Duration |
| 5 | + 与 VF 收益由独立观测记录。 | ||
| 5 | tags: | 6 | tags: |
| 6 | - task.implementation | 7 | - task.implementation |
| 7 | - topic.architecture | 8 | - topic.architecture |
| @@ -11,11 +12,12 @@ sources: | |||
| 11 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md | 12 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md |
| 12 | role: primary | 13 | role: primary |
| 13 | created_at: '2026-04-14T00:00:00Z' | 14 | created_at: '2026-04-14T00:00:00Z' |
| 14 | -updated_at: '2026-09-03T13:55:44Z' | 15 | +updated_at: '2026-10-22T10:35:31Z' |
| 15 | platforms: | 16 | platforms: |
| 17 | +- a2 | ||
| 18 | +- a3 | ||
| 16 | - '950' | 19 | - '950' |
| 17 | --- | 20 | --- |
| 18 | - | ||
| 19 | # PyPTO | 21 | # PyPTO |
| 20 | 22 | ||
| 21 | ## Summary | 23 | ## Summary |
| @@ -48,7 +50,7 @@ platforms: | |||
| 48 | 50 | ||
| 49 | ## DeepSeek-V4 框架级增强 | 51 | ## DeepSeek-V4 框架级增强 |
| 50 | 52 | ||
| 51 | -DeepSeek-V4 0day 适配在 PyPTO 上一次性发布了 `hc_pre`、`Compressor`、三个 FA 类算子(`SWA` / `CFA` / `SCFA`),并对 `MlaProlog` / `IndexerProlog` 做了新架构适配。实现代码位于 `ops/pypto_python/impl/`(compressor_pypto.py / hc_pre_pypto.py / mla_prolog_pypto.py / sliding_window_attention_pypto.py / compressed_flash_attention_pypto.py / sparse_compress_flash_attention_pypto.py / lightning_indexer_prolog_quant_pypto.py 等)。guide 报告各融合算子代码规模为 0.2–0.9 KLOC、开发周期 3–7 天;单 die / 64 batch / MTP1 / 8k 序列下,典型算子耗时为:SWA 65us / CFA 90us / SCFA 160us / mla_prolog 130us / hc_pre 70us / lightning_indexer_prolog 101us。 | 53 | +DeepSeek-V4 0day 适配在 PyPTO 上一次性发布了 `hc_pre`、`Compressor`、三个 FA 类算子(`SWA` / `CFA` / `SCFA`),并对 `MlaProlog` / `IndexerProlog` 做了新架构适配。实现代码位于 `ops/pypto_python/impl/`(compressor_pypto.py / hc_pre_pypto.py / mla_prolog_pypto.py / sliding_window_attention_pypto.py / compressed_flash_attention_pypto.py / sparse_compress_flash_attention_pypto.py / lightning_indexer_prolog_quant_pypto.py 等)。源码规模、开发周期与各算子 Duration 属该来源的一次披露,统一见[平台待核验观测](../../optimizations/benchmark_observations/pypto_v4_operator_source_observations.md)。 |
| 52 | 54 | ||
| 53 | ### PTO-ISA:跨代际芯片兼容(兼容不降速) | 55 | ### PTO-ISA:跨代际芯片兼容(兼容不降速) |
| 54 | 56 | ||
| @@ -67,11 +69,11 @@ PyPTO 后端基于 **PTO 虚拟指令集(PTO ISA)** 打造。算子前端无 | |||
| 67 | - **切 K MatMul**:Decode 场景 MatMul 的 K 轴很长(如 hc_pre 16K)而 M/N 轴较小,采用 `pypto.set_cube_tile_shapes(..., enable_split_k=True, enable_multi_data_load=True)` 切 K(实现位置:hc_pre_pypto.py:176)把数据切到不同核充分利用算力;shape 大时开大包搬运提带宽利用率。 | 69 | - **切 K MatMul**:Decode 场景 MatMul 的 K 轴很长(如 hc_pre 16K)而 M/N 轴较小,采用 `pypto.set_cube_tile_shapes(..., enable_split_k=True, enable_multi_data_load=True)` 切 K(实现位置:hc_pre_pypto.py:176)把数据切到不同核充分利用算力;shape 大时开大包搬运提带宽利用率。 |
| 68 | - **免广播 / scatter_update / 转置技巧**:Sinkhorn-Knopp 的归约串联逐元素乘除经 `pypto.experimental.set_operation_config(combine_axis=True)`(实现位置:hc_pre_pypto.py:136)避免显式广播;Compressor 的 `kv_state`/`score_state` 刷新用 `scatter_update_3d`(实现位置:compressor_pypto.py:453,611)规避反复搬入 UB + concat;hc_pre Prefill 用 `hc_fn^T @ x^T` 转置技巧把归约轴置于 -2/-3 提效。各算子级细节见对应 kernel 页。 | 70 | - **免广播 / scatter_update / 转置技巧**:Sinkhorn-Knopp 的归约串联逐元素乘除经 `pypto.experimental.set_operation_config(combine_axis=True)`(实现位置:hc_pre_pypto.py:136)避免显式广播;Compressor 的 `kv_state`/`score_state` 刷新用 `scatter_update_3d`(实现位置:compressor_pypto.py:453,611)规避反复搬入 UB + concat;hc_pre Prefill 用 `hc_fn^T @ x^T` 转置技巧把归约轴置于 -2/-3 提效。各算子级细节见对应 kernel 页。 |
| 69 | 71 | ||
| 70 | -### 毕昇 PTO 编译器 VF 自动融合(hc_post +22.7%,带平台前提) | 72 | +### 毕昇 PTO 编译器 VF 自动融合 |
| 71 | 73 | ||
| 72 | -`hc_post`(仅用 Vector 核,计算式 `y = post.unsqueeze(-1)*x.unsqueeze(-2) + sum(comb.unsqueeze(-1)*residual.unsqueeze(-2), dim=2)`)在已做其他优化基础上,PTO-ISA 结合毕昇 PTO 编译器的 **VF(Vector Fusion)自动融合**能力,做 micro kernel 级融合,相比未使能自动融合性能**提升 22.7%**。 | 74 | +`hc_post`(仅用 Vector 核,计算式 `y = post.unsqueeze(-1)*x.unsqueeze(-2) + sum(comb.unsqueeze(-1)*residual.unsqueeze(-2), dim=2)`)在已做其他优化基础上,PTO-ISA 结合毕昇 PTO 编译器的 **VF(Vector Fusion)自动融合**能力,做 micro kernel 级融合,该来源报告过相对收益,数字统一由独立观测维护。 |
| 73 | 75 | ||
| 74 | -> ⚠️ **平台前提(待验证完整 shape)**:+22.7% 为 guide 给出的单算子相对收益(相比未使能自动融合),未给完整 shape 取值与端到端收益。VF 融合属外部毕昇 PTO 编译器能力,cann-recipes-infer 不含该编译器源码。cann-recipes-infer 的 `hc_post` 另有 **AscendC 手写实现**(`ops/ascendc/src/hc_post/`),PyPTO 侧 hc_post 未单独建实现文件 —— 二者并列,见 [mhc-hcpre-hcpost](../kernels/mhc_hcpre_hcpost.md)。 | 76 | +> 该单算子结果未绑定具体测量型号、完整 shape 与端到端收益,见[来源观测](../../optimizations/benchmark_observations/pypto_v4_operator_source_observations.md)。VF 融合属外部毕昇 PTO 编译器能力,cann-recipes-infer 不含该编译器源码。cann-recipes-infer 的 `hc_post` 另有 **AscendC 手写实现**(`ops/ascendc/src/hc_post/`),PyPTO 侧 hc_post 未单独建实现文件 —— 二者并列,见 [mhc-hcpre-hcpost](../kernels/mhc_hcpre_hcpost.md)。 |
| 75 | 77 | ||
| 76 | ## 机制落地(V3.2-Exp PyPTO 算子开发指南) | 78 | ## 机制落地(V3.2-Exp PyPTO 算子开发指南) |
| 77 | 79 | ||
| @@ -141,4 +143,4 @@ Tiling 得到 Tile Operation 粒度的计算图后,框架对计算图切分并 | |||
| 141 | - [mla-prolog](../kernels/mla_prolog.md)(V4 MLAProlog 适配为 PyPTO 算子:inplace interleaved RoPE、Unrollist 分档、合图) | 143 | - [mla-prolog](../kernels/mla_prolog.md)(V4 MLAProlog 适配为 PyPTO 算子:inplace interleaved RoPE、Unrollist 分档、合图) |
| 142 | - [indexer-prolog](../kernels/indexer_prolog.md)(V3.2-Exp `npu_lightning_indexer_prolog_pto` 大融合算子;V4 CSA 层 IndexerProlog q_hadamard / Quant 流程) | 144 | - [indexer-prolog](../kernels/indexer_prolog.md)(V3.2-Exp `npu_lightning_indexer_prolog_pto` 大融合算子;V4 CSA 层 IndexerProlog q_hadamard / Quant 流程) |
| 143 | - [cann-shmem](../techniques/cann_shmem.md)(pyPTO-shmem 框架接入 SHMEM:以 `shmem_put`/`shmem_get`/`ShmemSignal` 等图记法做通算融合算子) | 145 | - [cann-shmem](../techniques/cann_shmem.md)(pyPTO-shmem 框架接入 SHMEM:以 `shmem_put`/`shmem_get`/`ShmemSignal` 等图记法做通算融合算子) |
| 144 | -- [shmem-dispatch-combine](../techniques/shmem_dispatch_combine.md)(pyPTO-shmem 写 MoE dispatch&combine:dispatch 三子图 / combine 两子图,ShmemPut/WaitUntil/ShmemGet/Matmul 加权融合) | 146 | +- [shmem-dispatch-combine](../techniques/shmem_dispatch_combine.md)(pyPTO-shmem 写 MoE dispatch&combine:dispatch 三子图 / combine 两子图,ShmemPut/WaitUntil/ShmemGet/Matmul 加权融合) |
| @@ -1,7 +1,10 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: Deepseek Indexer Attention Kernel | 3 | title: Deepseek Indexer Attention Kernel |
| 4 | -description: '`Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" 能力在 DeepSeek-V3.2-Exp 上的典型实例。 PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。' | 4 | +description: '`Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** |
| 5 | + 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer | ||
| 6 | + + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" | ||
| 7 | + 能力在 DeepSeek-V3.2-Exp 上的典型实例。 PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。' | ||
| 5 | tags: | 8 | tags: |
| 6 | - task.implementation | 9 | - task.implementation |
| 7 | - topic.attention | 10 | - topic.attention |
| @@ -13,16 +16,19 @@ sources: | |||
| 13 | role: primary | 16 | role: primary |
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md | 17 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md |
| 15 | role: supporting | 18 | role: supporting |
| 19 | +- resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/ops/pypto/docs/deepseek_indexer_attention.md | ||
| 20 | + role: constraint | ||
| 16 | created_at: '2026-04-14T00:00:00Z' | 21 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 22 | +updated_at: '2026-10-22T10:35:31Z' |
| 18 | platforms: | 23 | platforms: |
| 19 | -- agnostic | 24 | +- a3 |
| 20 | --- | 25 | --- |
| 21 | - | ||
| 22 | # Deepseek Indexer Attention Kernel | 26 | # Deepseek Indexer Attention Kernel |
| 23 | 27 | ||
| 24 | ## Summary | 28 | ## Summary |
| 25 | 29 | ||
| 30 | +本卡覆盖固定版本 PyPTO DIA 的 Atlas A3 实现支持范围;支持平台不等于每组性能观测都有完整测量环境。 | ||
| 31 | + | ||
| 26 | `Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" 能力在 DeepSeek-V3.2-Exp 上的典型实例。 | 32 | `Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" 能力在 DeepSeek-V3.2-Exp 上的典型实例。 |
| 27 | 33 | ||
| 28 | PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。 | 34 | PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。 |
| @@ -45,7 +51,7 @@ PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_inde | |||
| 45 | 51 | ||
| 46 | 四个关键性能瓶颈(源文档): | 52 | 四个关键性能瓶颈(源文档): |
| 47 | 53 | ||
| 48 | -1. **离散 Gather 部分耗时占比 >70%**:SFA 根据 LI 产出的 TopK index 从 `kv_cache/kr_cache` 离散读取对应 KV —— 原实现在 Vector 核完成"离散到连续"转换后写 GM Workspace,Cube 核再搬到 L1 使用,**既浪费内存又重复搬运**。 | 54 | +1. **离散 Gather 是来源泳道图中的主要瓶颈**:SFA 根据 LI 产出的 TopK index 从 `kv_cache/kr_cache` 离散读取对应 KV —— 原实现在 Vector 核完成"离散到连续"转换后写 GM Workspace,Cube 核再搬到 L1 使用,**既浪费内存又重复搬运**。 |
| 49 | 2. **Indexer Prolog matmul 任务过多**:M 轴为 1,未合轴 → Matmul 利用率过低。 | 55 | 2. **Indexer Prolog matmul 任务过多**:M 轴为 1,未合轴 → Matmul 利用率过低。 |
| 50 | 3. **流水线 bubble 较大**:两个子图之间存在未消除的 barrier,后续无依赖 task 也被停滞。 | 56 | 3. **流水线 bubble 较大**:两个子图之间存在未消除的 barrier,后续无依赖 task 也被停滞。 |
| 51 | 4. **Reshape 空子图导致启动延迟**:未充分优化的 reshape 形成空子图,依赖其结果的 task 停滞。 | 57 | 4. **Reshape 空子图导致启动延迟**:未充分优化的 reshape 形成空子图,依赖其结果的 task 停滞。 |
| @@ -99,4 +105,7 @@ DeepSeek-V3.2-Exp 的 Future Plan 明示:"MegaKernel:Decode 阶段仍然存 | |||
| 99 | - [indexer-prolog](indexer_prolog.md) | 105 | - [indexer-prolog](indexer_prolog.md) |
| 100 | - [mla-prolog](mla_prolog.md) | 106 | - [mla-prolog](mla_prolog.md) |
| 101 | - [pypto](../frameworks/pypto.md) | 107 | - [pypto](../frameworks/pypto.md) |
| 102 | -- [dsa](../modules/dsa.md)(DSA 架构模式,DIA 是其 LI+Prolog+SFA 融合的算子级实现) | 108 | +- [dsa](../modules/dsa.md)(DSA 架构模式,DIA 是其 LI+Prolog+SFA 融合的算子级实现) |
| 109 | +## 定量观测入口 | ||
| 110 | + | ||
| 111 | +该来源对旧实现的 Gather 耗时占比披露统一见[分组算子观测](../../optimizations/benchmark_observations/operator_tuning_source_observations.md),不推导其他负载的固定占比。 | ||
| @@ -1,14 +1,14 @@ | |||
| 1 | # 融合 Kernel | 1 | # 融合 Kernel |
| 2 | 2 | ||
| 3 | - [Compressor (C4A / C128A KV 压缩融合算子)](compressor.md):Compressor 是 DeepSeek-V4 的 KV 压缩融合算子,将 Linear、分组 softmax、Hadamard/reduce、RMSNorm 和 Partial RoPE 合并为单一算子,为 SAS 与 Lightning Indexer 生成压缩缓存。C4A 以 4:1 压缩 CSA,需处理相邻组 overlap 和错位写入;C128A 以 128:1 压缩 HCA。AscendC 在 A3 采用不同 C4A/C128A 基本块与双缓冲,950PR/DT 使用 256³ 基本块;PyPTO 按 compress_ratio/rotate 分支并以 split-K、子图分区和 scatter_update_3d 刷新状态。Epilog 负责量化、拼接 scale 并写回 KV/Indexer Cache,FP8 与非 FP8 路径不同。 | 3 | - [Compressor (C4A / C128A KV 压缩融合算子)](compressor.md):Compressor 是 DeepSeek-V4 的 KV 压缩融合算子,将 Linear、分组 softmax、Hadamard/reduce、RMSNorm 和 Partial RoPE 合并为单一算子,为 SAS 与 Lightning Indexer 生成压缩缓存。C4A 以 4:1 压缩 CSA,需处理相邻组 overlap 和错位写入;C128A 以 128:1 压缩 HCA。AscendC 在 A3 采用不同 C4A/C128A 基本块与双缓冲,950PR/DT 使用 256³ 基本块;PyPTO 按 compress_ratio/rotate 分支并以 split-K、子图分区和 scatter_update_3d 刷新状态。Epilog 负责量化、拼接 scale 并写回 KV/Indexer Cache,FP8 与非 FP8 路径不同。 |
| 4 | -- [Deepseek Indexer Attention Kernel](deepseek_indexer_attention.md):`Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" 能力在 DeepSeek-V3.2-Exp 上的典型实例。 PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。 | 4 | +- [Deepseek Indexer Attention Kernel机制](deepseek_indexer_attention.md):`Deepseek Indexer Attention` (DIA) 是 cann-recipes-infer 用 **PyPTO** 实现的**大融合算子** —— 把 DSA 的 4 个相对独立子算子 MLAProlog + IndexerProlog + Lightning Indexer + Sparse Flash Attention 融合成单一 kernel,在整网范围内挖掘更大并行度、提升内存利用率。这是 PyPTO "MegaKernel" 能力在 DeepSeek-V3.2-Exp 上的典型实例。 PyPTO 源码:`ops/pypto/src/deepseek_indexer_attention/op_kernel/deepseek_indexer_attention.cpp`。 |
| 5 | - [npu_fused_infer_attention_score (v1 / v2)](fused_infer_attention_score.md):昇腾推理侧的通用 FlashAttention 融合算子(简称 **FIA**),覆盖 Softmax-Attention 的主体计算:`softmax(QKᵀ/√d)·V`,支持 Paged KVCache、Block-Table、分离的 NoPE/RoPE Query/Key 输入、以及 Query / Key / Value 三端的动态 Int8 反量化(dequant_scale_*)。在 DeepSeek-R1 decode 路径中作为 mla-prolog 之后的主 FA 算子。 v2 相对 v1 增加了 TND_NTD 布局支持、更灵活的 sparse_mode、分离的 NoPE/RoPE Key 路径与 C8 量化反量化入口。 | 5 | - [npu_fused_infer_attention_score (v1 / v2)](fused_infer_attention_score.md):昇腾推理侧的通用 FlashAttention 融合算子(简称 **FIA**),覆盖 Softmax-Attention 的主体计算:`softmax(QKᵀ/√d)·V`,支持 Paged KVCache、Block-Table、分离的 NoPE/RoPE Query/Key 输入、以及 Query / Key / Value 三端的动态 Int8 反量化(dequant_scale_*)。在 DeepSeek-R1 decode 路径中作为 mla-prolog 之后的主 FA 算子。 v2 相对 v1 增加了 TND_NTD 布局支持、更灵活的 sparse_mode、分离的 NoPE/RoPE Key 路径与 C8 量化反量化入口。 |
| 6 | - [GatherSelectionKvCache](gather_selection_kv_cache.md):`npu_gather_selection_kv_cache` 是 cann-recipes-infer 为 DeepSeek-V3.2-Exp **KVCache Offload** 场景新开发的 AscendC 自定义算子。在 Decode 阶段,根据 lightning-indexer 返回的 TopK(2048)个相关 token index,**从 Host 内存上的 Full KVCache 中选出参与计算的 KV 并搬回设备**,再喂给 sparse-flash-attention。 其核心优化是利用**前后 Token 在同一层上 TopK 命中率(相似度)特性**复用设备上已有 cache:若当前步 TopK 与前一步 TopK 有 60% 重叠,仅从 Host 读取 40% 未复用的 KV,60% 直接从设备上读,显著减少 H2D 耗时。 算子源码:`ops/ascendc/src/gather_selection_kv_cache/`。自定义算子文档 `ops/ascendc/docs/custom-npu_gather_selection_kv_cache.md`。 | 6 | - [GatherSelectionKvCache](gather_selection_kv_cache.md):`npu_gather_selection_kv_cache` 是 cann-recipes-infer 为 DeepSeek-V3.2-Exp **KVCache Offload** 场景新开发的 AscendC 自定义算子。在 Decode 阶段,根据 lightning-indexer 返回的 TopK(2048)个相关 token index,**从 Host 内存上的 Full KVCache 中选出参与计算的 KV 并搬回设备**,再喂给 sparse-flash-attention。 其核心优化是利用**前后 Token 在同一层上 TopK 命中率(相似度)特性**复用设备上已有 cache:若当前步 TopK 与前一步 TopK 有 60% 重叠,仅从 Host 读取 40% 未复用的 KV,60% 直接从设备上读,显著减少 H2D 耗时。 算子源码:`ops/ascendc/src/gather_selection_kv_cache/`。自定义算子文档 `ops/ascendc/docs/custom-npu_gather_selection_kv_cache.md`。 |
| 7 | -- [Indexer Prolog Kernel](indexer_prolog.md):`Indexer Prolog` 是 LightningIndexer 之前的准备算子,为 LI 提供输入 q、weight、q_scale,处理 Indexer Q / Indexer Cache(K) / Indexer Weight 三条独立计算流(各含 Matmul/RoPE/LayerNorm/Hadamard/动态 Per-Token-Head 量化)。DeepSeek-V3.2-Exp(DSA):PyPTO 单一大融合算子 npu_lightning_indexer_prolog_pto,4 batch×MTP1×64K 逼近理论极限(Indexer Q 四段理论拆解、瓶颈在搬运、Q 掩盖 Cache/Weight);配合 torchair npu_stream_switch 多流。DeepSeek-V4 适配(PyPTO):IndexerProlog 仅出现在 compress_ratio=4 的 CSA 层(C128A/HCA 无 LI 模块,代码 cmp_ratio:4 only c4a have li module)。Query q_hadamard/Quant 流程:q_tmp=qr@idx_wq_b·scale → q_hadamard=Cat(q_tmp[:,:nope_dim],Rope(q_tmp[:,nope_dim:]))@hadamard → q,q_scale=Quant;代码实际展开为 wq_b→inplace_partial_rotary_mul(interleave)→rotate_activation(hadamard_matrix=get_had_pow2)→量化(int8 走 npu_dynamic_quant 转 FP16 / FP8 走 npu_dynamic_block_quant)。Weights=x@weights_proj·1/sqrt(idx_nq·head_dim)(代码 softmax_scale=head_dim**-0.5·n_heads**-0.5 一致),不量化转 FP16/FP32。doc 描述 fused 而 code 为展开步骤(V4 图模式是否回落单一融合 PyPTO 算子源未明示,待验证)。 | 7 | +- [Indexer Prolog Kernel机制](indexer_prolog.md):`Indexer Prolog` 是 LightningIndexer 之前的准备算子,为 LI 提供输入 q、weight、q_scale,处理 Indexer Q / Indexer Cache(K) / Indexer Weight 三条独立计算流(Q 不含 LayerNorm,K 含 LayerNorm;Q/K 涉及 RoPE、Hadamard 与动态量化,Weight 仅投影与缩放且不量化)。DeepSeek-V3.2-Exp(DSA):PyPTO 单一大融合算子 npu_lightning_indexer_prolog_pto,4 batch×MTP1×64K 逼近理论极限(Indexer Q 四段理论拆解、瓶颈在搬运、Q 掩盖 Cache/Weight);配合 torchair npu_stream_switch 多流。DeepSeek-V4 适配(PyPTO):IndexerProlog 仅出现在 compress_ratio=4 的 CSA 层(C128A/HCA 无 LI 模块,代码 cmp_ratio:4 only c4a have li module)。Query q_hadamard/Quant 流程:q_tmp=qr@idx_wq_b·scale → q_hadamard=Cat(q_tmp[:,:nope_dim],Rope(q_tmp[:,nope_dim:]))@hadamard → q,q_scale=Quant;代码实际展开为 wq_b→inplace_partial_rotary_mul(interleave)→rotate_activation(hadamard_matrix=get_had_pow2)→量化(int8 走 npu_dynamic_quant 转 FP16 / FP8 走 npu_dynamic_block_quant)。Weights=x@weights_proj·1/sqrt(idx_nq·head_dim)(代码 softmax_scale=head_dim**-0.5·n_heads**-0.5 一致),不量化转 FP16/FP32。doc 描述 fused 而 code 为展开步骤(V4 图模式是否回落单一融合 PyPTO 算子源未明示,待验证)。 |
| 8 | -- [Lightning Indexer Kernel](lightning_indexer.md):`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token 精确(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention,将 Top-k 计算融合进单一算子(相较外置收益 ~20%)。K 随模型而异:V3.2-Exp DSA 在完整上下文选 TopK=2048 喂给 SparseFlashAttention;V4 仅在 C4A(compress_ratio=4,CSA)层对 Compressor 压缩后 cmp_KV 做 Top-k(运行时取 config.index_topk,guide 例 512)喂给 SAS 的 SCFA(不同模型不同 K 并列保留)。提供 AscendC/PyPTO/TileLang 三路实现。V4 CSA 层增强:A3 LI Tiling 沿用 V3.2-Exp,适配 compress_ratio 阶梯形状 mask 产 cmp_sparse_indices;950PR/DT 量化版 LI(INT8/FP8 共用 torch.ops.custom.npu_quant_lightning_indexer,非独立 FP8 算子;FP8 区别在上游 q 经 npu_dynamic_block_quant 量化、INT8 经 npu_dynamic_quant);Top-k 两套平台分支并列:Atlas-A3 sort32+mrgsort(S_k=16384,k=512 约 28375 cycle,沿用 V3.2-Exp)/ 950PR/DT Histograms 直方图(保序变换+多级直方图分桶 value_k=a1·2^24+a2·2^16+a3·2^8+a4+向量化筛选,理论 (S_k/256·111+192) cycle,S_k=16384 约 7296 cycle,cycle 前提为 950PR/DT 平台)。阶梯 mask 形状参数源未给(待验证)。 | 8 | +- [Lightning Indexer Kernel机制](lightning_indexer.md):`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token 精确(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention,将 Top-k 计算融合进单一算子(具体收益见平台限定的来源观测)。K 随模型而异:V3.2-Exp DSA 在完整上下文选 TopK=2048 喂给 SparseFlashAttention;V4 仅在 C4A(compress_ratio=4,CSA)层对 Compressor 压缩后 cmp_KV 做 Top-k(运行时取 config.index_topk,guide 例 512)喂给 SAS 的 SCFA(不同模型不同 K 并列保留)。提供 AscendC/PyPTO/TileLang 三路实现。V4 CSA 层增强:A3 LI Tiling 沿用 V3.2-Exp,适配 compress_ratio 阶梯形状 mask 产 cmp_sparse_indices;950PR/DT 量化版 LI(INT8/FP8 共用 torch.ops.custom.npu_quant_lightning_indexer,非独立 FP8 算子;FP8 区别在上游 q 经 npu_dynamic_block_quant 量化、INT8 经 npu_dynamic_quant);Top-k 两套平台分支并列:Atlas-A3 sort32+mrgsort(S_k=16384,k=512 约 28375 cycle,沿用 V3.2-Exp)/ 950PR/DT Histograms 直方图(保序变换+多级直方图分桶 value_k=a1·2^24+a2·2^16+a3·2^8+a4+向量化筛选,理论 (S_k/256·111+192) cycle,S_k=16384 约 7296 cycle,cycle 前提为 950PR/DT 平台)。阶梯 mask 形状参数源未给(待验证)。 |
| 9 | - [mambav2_rmsnormgated](mambav2_rmsnormgated.md):AscendC 实现的 Mamba v2 风格 RMSNorm + SiLU(z) + Mul 融合算子,Qwen3-Next gated-deltanet 输出段使用。BF16 输入输出(`x`, `z`, `w`),FP32 内部计算,单 AIV kernel 完成归一化、门控激活、权重缩放和逐元素相乘,消除三次独立 kernel launch 和两次中间 BF16 转存。 | 9 | - [mambav2_rmsnormgated](mambav2_rmsnormgated.md):AscendC 实现的 Mamba v2 风格 RMSNorm + SiLU(z) + Mul 融合算子,Qwen3-Next gated-deltanet 输出段使用。BF16 输入输出(`x`, `z`, `w`),FP32 内部计算,单 AIV kernel 完成归一化、门控激活、权重缩放和逐元素相乘,消除三次独立 kernel launch 和两次中间 BF16 转存。 |
| 10 | -- [HcPre / HcPost (mHC 融合算子)](mhc_hcpre_hcpost.md):`HcPre` 与 `HcPost` 是 DeepSeek-V4 mHC 残差结构的成对融合算子,完成 Attention/MoE 子层前后的多路↔单路变换。HcPre 融合 RMSNorm、hc_fn 线性变换、Sinkhorn-Knopp 和 pre 加权求和,输出 y、post、comb;HcPost 用 post 扩展子层输出并与 comb 残差合并,目标是让中间数据留在 UBuffer。A3 AscendC 采用 Cube:Vector=1:2 流水,Decode 切 K、Prefill 切 token;PyPTO 以 MPMD 并行 Vector RMSNorm 与 Cube MatMul,并用 split-K 和静态展开。hc_post 的 VF 报告提升 22.7%。Native 与 PyPTO 的首轮 Sinkhorn 归一顺序不同,应以各自代码为准。 | 10 | +- [HcPre / HcPost (mHC 融合算子)](mhc_hcpre_hcpost.md):`HcPre` 与 `HcPost` 是 DeepSeek-V4 mHC 残差结构的成对融合算子,完成 Attention/MoE 子层前后的多路↔单路变换。HcPre 融合 RMSNorm、hc_fn 线性变换、Sinkhorn-Knopp 和 pre 加权求和,输出 y、post、comb;HcPost 用 post 扩展子层输出并与 comb 残差合并,目标是让中间数据留在 UBuffer。A3 AscendC 采用 Cube:Vector=1:2 流水,Decode 切 K、Prefill 切 token;PyPTO 以 MPMD 并行 Vector RMSNorm 与 Cube MatMul,并用 split-K 和静态展开。VF 的历史测量另见来源观测。Native 与 PyPTO 的首轮 Sinkhorn 均先行归一再列归一,数值实现细节以各自代码为准。 |
| 11 | - [npu_mla_prolog (v2 / v3)](mla_prolog.md):`npu_mla_prolog` 是 DeepSeek-V3/R1/Kimi-K2 的 MLA 前置融合算子,在单个 kernel 中完成 Q、KV 两级投影、RMSNorm、RoPE,并按 cache_index 写入 Paged NZ KV Cache,以减少 Decode 下发和 kernel gap。R1 Decode 使用 v3 接口,支持权重、KV Cache 和 Query 量化模式,并与 FIA v2 配合。DeepSeek-V4 虽改为 MQA/Shared-KV,仍以 PyPTO 版本完成 Q/KV 前置投影,加入 interleaved Partial RoPE、动态 shape 静态展开、buffer 合图、split-K 和 MPMD 流水。独立的 AscendC RoPE+Quant 算子尚未证实接入生产路径,v2/v3 的完整语义差异也仍待确认。 | 11 | - [npu_mla_prolog (v2 / v3)](mla_prolog.md):`npu_mla_prolog` 是 DeepSeek-V3/R1/Kimi-K2 的 MLA 前置融合算子,在单个 kernel 中完成 Q、KV 两级投影、RMSNorm、RoPE,并按 cache_index 写入 Paged NZ KV Cache,以减少 Decode 下发和 kernel gap。R1 Decode 使用 v3 接口,支持权重、KV Cache 和 Query 量化模式,并与 FIA v2 配合。DeepSeek-V4 虽改为 MQA/Shared-KV,仍以 PyPTO 版本完成 Q/KV 前置投影,加入 interleaved Partial RoPE、动态 shape 静态展开、buffer 合图、split-K 和 MPMD 流水。独立的 AscendC RoPE+Quant 算子尚未证实接入生产路径,v2/v3 的完整语义差异也仍待确认。 |
| 12 | - [recurrent_gated_delta_rule](recurrent_gated_delta_rule.md):Decode/MTP 阶段 Gated DeltaNet 的融合算子,用于混合注意力模型(Qwen3-Next、Qwen3.5-MoE)线性注意力层的单步/少量 draft token 递推。一次 kernel launch 完成 Sigmoid + L2Norm + Gated Delta 递推注意力 + ssm_state 选择性更新,消除 Decode GDN 路径多个小算子的 Launch 和中间搬运开销。BF16 输入输出、FP32 累加,AIV 单元执行。Qwen3-Next 通过 AscendC 自研算子使用;Qwen3.5-MoE 通过 torch_npu.npu_recurrent_gated_delta_rule 调用同名算子。 | 12 | - [recurrent_gated_delta_rule](recurrent_gated_delta_rule.md):Decode/MTP 阶段 Gated DeltaNet 的融合算子,用于混合注意力模型(Qwen3-Next、Qwen3.5-MoE)线性注意力层的单步/少量 draft token 递推。一次 kernel launch 完成 Sigmoid + L2Norm + Gated Delta 递推注意力 + ssm_state 选择性更新,消除 Decode GDN 路径多个小算子的 Launch 和中间搬运开销。BF16 输入输出、FP32 累加,AIV 单元执行。Qwen3-Next 通过 AscendC 自研算子使用;Qwen3.5-MoE 通过 torch_npu.npu_recurrent_gated_delta_rule 调用同名算子。 |
| 13 | -- [SparseAttnSharedKV (SAS) Kernel](sparse_attn_sharedkv.md):`SparseAttnSharedKV`(SAS)是 DeepSeek-V4 的统一 Shared-KV Attention 融合算子,一套接口覆盖滑窗、压缩及压缩后稀疏 Attention,并原生支持 Attention Sink。A3 非量化路径采用 QK MatMul→online softmax→PV MatMul→rescale 四阶段;950PR/DT 伪量化路径增加前置反量化,共五阶段。场景由原始/压缩 KV、窗口、压缩比、稀疏索引、sink 和 metadata 等参数组合控制,AICPU metadata 算子负责值依赖分核。PyPTO 分为 SWA、CFA、SCFA 三种场景,TileLang 另有 block=64 的 V4-Flash 实现。SAS 不等同于 V3.2-Exp DSA 使用的 SparseFlashAttention。 | 13 | +- [SparseAttnSharedKV (SAS) Kernel机制](sparse_attn_sharedkv.md):`SparseAttnSharedKV`(SAS)是 DeepSeek-V4 的统一 Shared-KV Attention 融合算子,一套接口覆盖滑窗、压缩及压缩后稀疏 Attention,并原生支持 Attention Sink。A3 非量化路径采用 QK MatMul→online softmax→PV MatMul→rescale 四阶段;950PR/DT 伪量化路径增加前置反量化,共五阶段。场景由原始/压缩 KV、窗口、压缩比、稀疏索引、sink 和 metadata 等参数组合控制,AICPU metadata 算子负责值依赖分核。PyPTO 分为 SWA、CFA、SCFA 三种场景,TileLang 另有 block=64 的 V4-Flash 实现。SAS 不等同于 V3.2-Exp DSA 使用的 SparseFlashAttention。 |
| 14 | -- [Sparse Flash Attention Kernel](sparse_flash_attention.md):`SparseFlashAttention` 是 DeepSeek-V3.2-Exp **DSA** 的主 Attention 算子 —— 在 lightning-indexer 产出的 TopK index 基础上,`Gather` 出稀疏 KV 子集并执行 Flash Attention 计算。沿用 FlashAttention 的 `C_1 (Q@K^T) → V_1 (online softmax) → C_2 (P@V) → V_2 (rescale)` 四阶段流程,但面向**离散访存**进行了专门优化。模块语义见 sparse-flash-attention。 cann-recipes-infer 提供 AscendC + TileLang 两路实现;PyPTO 侧则作为 deepseek-indexer-attention 大融合算子内的子流程出现。 | 14 | +- [Sparse Flash Attention Kernel机制](sparse_flash_attention.md):`SparseFlashAttention` 是 DeepSeek-V3.2-Exp **DSA** 的主 Attention 算子 —— 在 lightning-indexer 产出的 TopK index 基础上,`Gather` 出稀疏 KV 子集并执行 Flash Attention 计算。沿用 FlashAttention 的 `C_1 (Q@K^T) → V_1 (online softmax) → C_2 (P@V) → V_2 (rescale)` 四阶段流程,但面向**离散访存**进行了专门优化。模块语义见 sparse-flash-attention。 cann-recipes-infer 提供 AscendC + TileLang 两路实现;PyPTO 侧则作为 deepseek-indexer-attention 大融合算子内的子流程出现。 |
| @@ -1,7 +1,16 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: Indexer Prolog Kernel | 3 | title: Indexer Prolog Kernel |
| 4 | -description: '`Indexer Prolog` 是 LightningIndexer 之前的准备算子,为 LI 提供输入 q、weight、q_scale,处理 Indexer Q / Indexer Cache(K) / Indexer Weight 三条独立计算流(各含 Matmul/RoPE/LayerNorm/Hadamard/动态 Per-Token-Head 量化)。DeepSeek-V3.2-Exp(DSA):PyPTO 单一大融合算子 npu_lightning_indexer_prolog_pto,4 batch×MTP1×64K 逼近理论极限(Indexer Q 四段理论拆解、瓶颈在搬运、Q 掩盖 Cache/Weight);配合 torchair npu_stream_switch 多流。DeepSeek-V4 适配(PyPTO):IndexerProlog 仅出现在 compress_ratio=4 的 CSA 层(C128A/HCA 无 LI 模块,代码 cmp_ratio:4 only c4a have li module)。Query q_hadamard/Quant 流程:q_tmp=qr@idx_wq_b·scale → q_hadamard=Cat(q_tmp[:,:nope_dim],Rope(q_tmp[:,nope_dim:]))@hadamard → q,q_scale=Quant;代码实际展开为 wq_b→inplace_partial_rotary_mul(interleave)→rotate_activation(hadamard_matrix=get_had_pow2)→量化(int8 走 npu_dynamic_quant 转 FP16 / FP8 走 npu_dynamic_block_quant)。Weights=x@weights_proj·1/sqrt(idx_nq·head_dim)(代码 softmax_scale=head_dim**-0.5·n_heads**-0.5 一致),不量化转 FP16/FP32。doc 描述 fused 而 code 为展开步骤(V4 图模式是否回落单一融合 PyPTO 算子源未明示,待验证)。' | 4 | +description: '`Indexer Prolog` 是 LightningIndexer 之前的准备算子,为 LI 提供输入 q、weight、q_scale,处理 |
| 5 | + Indexer Q / Indexer Cache(K) / Indexer Weight 三条独立计算流(Q 不含 LayerNorm,K 含 LayerNorm;Q/K 涉及 RoPE、Hadamard 与动态量化,Weight 仅投影与缩放且不量化)。DeepSeek-V3.2-Exp(DSA):PyPTO 单一大融合算子 npu_lightning_indexer_prolog_pto,4 | ||
| 6 | + batch×MTP1×64K 逼近理论极限(Indexer Q 四段理论拆解、瓶颈在搬运、Q 掩盖 Cache/Weight);配合 torchair npu_stream_switch | ||
| 7 | + 多流。DeepSeek-V4 适配(PyPTO):IndexerProlog 仅出现在 compress_ratio=4 的 CSA 层(C128A/HCA 无 | ||
| 8 | + LI 模块,代码 cmp_ratio:4 only c4a have li module)。Query q_hadamard/Quant 流程:q_tmp=qr@idx_wq_b·scale | ||
| 9 | + → q_hadamard=Cat(q_tmp[:,:nope_dim],Rope(q_tmp[:,nope_dim:]))@hadamard → q,q_scale=Quant;代码实际展开为 | ||
| 10 | + wq_b→inplace_partial_rotary_mul(interleave)→rotate_activation(hadamard_matrix=get_had_pow2)→量化(int8 | ||
| 11 | + 走 npu_dynamic_quant 转 FP16 / FP8 走 npu_dynamic_block_quant)。Weights=x@weights_proj·1/sqrt(idx_nq·head_dim)(代码 | ||
| 12 | + softmax_scale=head_dim**-0.5·n_heads**-0.5 一致),不量化转 FP16/FP32。doc 描述 fused 而 code | ||
| 13 | + 为展开步骤(V4 图模式是否回落单一融合 PyPTO 算子源未明示,待验证)。' | ||
| 5 | tags: | 14 | tags: |
| 6 | - task.implementation | 15 | - task.implementation |
| 7 | - topic.attention | 16 | - topic.attention |
| @@ -14,17 +23,16 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md | 23 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md |
| 15 | role: supporting | 24 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 25 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 26 | +updated_at: '2026-10-22T10:35:31Z' |
| 18 | platforms: | 27 | platforms: |
| 19 | - a3 | 28 | - a3 |
| 20 | - '950' | 29 | - '950' |
| 21 | --- | 30 | --- |
| 22 | - | ||
| 23 | # Indexer Prolog Kernel | 31 | # Indexer Prolog Kernel |
| 24 | 32 | ||
| 25 | ## Summary | 33 | ## Summary |
| 26 | 34 | ||
| 27 | -`Indexer Prolog` 是 [lightning-indexer](lightning_indexer.md) 之前的准备算子,为 LI 计算提供输入 `q`、`weight`(及 `q_scale`)。它处理三条独立计算流 —— **Indexer Q**、**Indexer Cache (K)**、**Indexer Weight** —— 每条流都包含 Matmul / RoPE / LayerNorm / Hadamard / 动态 Per-Token-Head 量化 等一连串 Linear 前后处理。 | 35 | +`Indexer Prolog` 是 [lightning-indexer](lightning_indexer.md) 之前的准备算子,为 LI 计算提供输入 `q`、`weight`(及 `q_scale`)。它处理三条独立计算流 —— **Indexer Q**、**Indexer Cache (K)**、**Indexer Weight** —— 三条流的操作并不相同:Q 流无 LayerNorm,K 流有 LayerNorm,Q/K 流进行相应 RoPE、Hadamard 与动态量化,Weight 流仅投影、缩放而不量化。 |
| 28 | 36 | ||
| 29 | - **DeepSeek-V3.2-Exp(DSA)**:cann-recipes-infer 通过 **PyPTO** 实现为单一大融合算子 `npu_lightning_indexer_prolog_pto`,典型 Shape(4 batch × MTP1 × 64K)下已**逼近理论极限性能**。PyPTO 源码:`ops/pypto/src/lightning_indexer_prolog_pto/op_kernel/`。 | 37 | - **DeepSeek-V3.2-Exp(DSA)**:cann-recipes-infer 通过 **PyPTO** 实现为单一大融合算子 `npu_lightning_indexer_prolog_pto`,典型 Shape(4 batch × MTP1 × 64K)下已**逼近理论极限性能**。PyPTO 源码:`ops/pypto/src/lightning_indexer_prolog_pto/op_kernel/`。 |
| 30 | - **DeepSeek-V4**:IndexerProlog **仅出现在 `compress_ratio = 4` 的 CSA 层**(C128A/HCA 层无 LI 模块),Query 计算走 q_hadamard / Quant 流程(int8 或 FP8),见 [DeepSeek-V4 适配](#deepseek-v4-适配pypto)。 | 38 | - **DeepSeek-V4**:IndexerProlog **仅出现在 `compress_ratio = 4` 的 CSA 层**(C128A/HCA 层无 LI 模块),Query 计算走 q_hadamard / Quant 流程(int8 或 FP8),见 [DeepSeek-V4 适配](#deepseek-v4-适配pypto)。 |
| @@ -158,83 +166,7 @@ $$weights = x @ weights\_proj \cdot \frac{1}{\sqrt{idx\_nq \cdot head\_dim}}$$ | |||
| 158 | 166 | ||
| 159 | ## 多流优化(case pattern) | 167 | ## 多流优化(case pattern) |
| 160 | 168 | ||
| 161 | -> 方法论总纲见 [multi-stream](../techniques/multi_stream.md)(`npu_stream_switch` tagged-stream / `npu_wait_tensor` / aclgraph tagged-event 的通用机制与适用判据)。这里聚焦 Indexer Prolog **前处理子链切流**这一具体模式。[Optimization Notes](#与多流优化配合) 已记 V3.2-Exp 「Cube/Vector 前处理 + `npu_stream_switch`」泳道示意;以下是 **GLM-5 / DeepSeek-V3.2-Exp `indexer.py` forward** 中实际落地的副流编排(与上文融合算子 `npu_lightning_indexer_prolog_pto` 是两套不同实现路径,不冲突)。 | 169 | +该固定实现的事件和分支代码统一维护在[独立实现案例](../../optimizations/cases/indexer_prolog_streams_fixed_implementation.md)。本卡保留机制、变体与接口;案例不能继承更宽的平台或收益范围。 |
| 162 | - | ||
| 163 | -### 动机 | ||
| 164 | - | ||
| 165 | -Indexer Prolog 的前处理由三条独立链组成(见 [三条独立流](#三条独立流)):Q 路径(`wq_b` Matmul → split q_pe/q_nope → RoPE → 拼回)、K 路径(`wk` → `k_norm` → RoPE)、Weight 路径(`weights_proj`)。在 `indexer.py` 这条**非单算子融合**的实现里,三链若全堆主流则串行成一段很长的前处理;而 Q Matmul(MTE/Cube bound)与 K-norm/Weight-proj(Vector/小 Matmul)硬件性质互补,存在 overlap 窗口。该模式仅在 **decode 阶段**启用(`enable_multi_streams = self.enable_multi_streams and not is_prefill`,dsv3.2 `indexer.py:249` / glm-5 `:169`),且只 overlap 前处理子链,**不是完整模块级双网络并行**(区别于 [moe-multi-stream](../techniques/moe_multi_stream.md) 的 MoE 双流)。 | ||
| 166 | - | ||
| 167 | -### 编排 | ||
| 168 | - | ||
| 169 | -- 副流 `"22"`:Q 路径。`wq_b(qr)` → `view` → `split` 出 `q_pe/q_nope` → 对 `q_pe` 做 RoPE → `cat` 回 `q`。dsv3.2 用 `torch_npu.npu_rotary_mul`,GLM-5 用 `torch_npu.npu_interleave_rope`(两库 RoPE 算子不同,详见关键代码)。 | ||
| 170 | -- 副流 `"33"`:Weight 路径,仅 `weights = self.weights_proj(x.view(-1, self.dim))`。 | ||
| 171 | -- 主流 `"0"`:K 路径 `wk → k_norm → split → RoPE → cat`,随后 wait Q 路径就绪再做 quant / 写 KV cache / 喂 Lightning Indexer。 | ||
| 172 | -- 依赖保证:进 `"22"` 前 `npu_wait_tensor(qr, query_states[0])`(Q 等 q_lora 投影就绪);进 `"33"` 前 `npu_wait_tensor(x, q_b)`(Weight 流让出,使 `"33"` 排在 `"22"` 的 `q_b` 之后)。**注意**:`npu_wait_tensor` 是 **eager(非 aclgraph)** 路径;aclgraph 路径改用 `npu_record_tagged_stream` + `npu_tagged_event_record/wait`(`indexer_npu_events[0..3]`)成对同步——二者按 `self.enable_aclgraph` 二选一。 | ||
| 173 | -- Q 量化(`q quant`:`apply_hadamard` + `npu_dynamic_quant`)在主流前处理完成、`tagged_event_wait(events[2])` 之后**再次进入 `"22"`** 与主流 K 量化重叠(glm-5 `:225` / dsv3.2 `:306` 区段)。 | ||
| 174 | - | ||
| 175 | -```mermaid | ||
| 176 | -%%{ init: { 'theme': 'neutral' } }%% | ||
| 177 | -flowchart LR | ||
| 178 | - subgraph S22["Stream 22 (Q)"] | ||
| 179 | - A["wq_b(qr)"] --> B["split q_pe/q_nope + RoPE -> cat q"] | ||
| 180 | - end | ||
| 181 | - subgraph S33["Stream 33 (Weight)"] | ||
| 182 | - C["weights_proj(x)"] | ||
| 183 | - end | ||
| 184 | - subgraph S0["Stream 0 主流 (K)"] | ||
| 185 | - D["wk -> k_norm -> split -> RoPE -> cat k"] | ||
| 186 | - E["wait Q ready"] | ||
| 187 | - F["q/k quant -> 写 KV cache -> Lightning Indexer"] | ||
| 188 | - D --> E --> F | ||
| 189 | - end | ||
| 190 | - A -. "npu_wait_tensor(x, q_b)" .-> C | ||
| 191 | - B -. "wait_tensor / tagged_event" .-> E | ||
| 192 | - C --> F | ||
| 193 | -``` | ||
| 194 | - | ||
| 195 | -### 关键代码 | ||
| 196 | - | ||
| 197 | -副流 `"22"` 跑 Q 路径(GLM-5 `models/glm-5/models/indexer.py:177`,RoPE 用 `npu_interleave_rope`): | ||
| 198 | - | ||
| 199 | -```python | ||
| 200 | -enable_multi_streams = self.enable_multi_streams and not is_prefill | ||
| 201 | -# ... | ||
| 202 | -with npu_stream_switch(enable_multi_streams, "22"): | ||
| 203 | - if enable_multi_streams: | ||
| 204 | - if self.enable_aclgraph: | ||
| 205 | - tng.ops.npu_tagged_event_wait(indexer_npu_events[0]) | ||
| 206 | - else: | ||
| 207 | - tng.scope.npu_wait_tensor(qr, query_states[0]) | ||
| 208 | - q_b = self.wq_b(qr, c8_input_dict.get("pertoken_scale", None)) # [b,s,1536]@[1536,64*128] | ||
| 209 | - q = q_b.view(bsz, seqlen, self.n_heads, self.head_dim) | ||
| 210 | - q_pe, q_nope = torch.split(q, [self.rope_head_dim, self.head_dim - self.rope_head_dim], dim=-1) | ||
| 211 | - q_pe = q_pe.view(-1, self.n_heads, 1, self.rope_head_dim) | ||
| 212 | - q_pe = torch_npu.npu_interleave_rope(q_pe, cos, sin).view(bsz, -1, self.n_heads, self.rope_head_dim) | ||
| 213 | - q = torch.cat([q_pe, q_nope], dim=-1) | ||
| 214 | -``` | ||
| 215 | - | ||
| 216 | -副流 `"33"` 提前跑 `weights_proj`(GLM-5 `:202`,定义在 `:90`): | ||
| 217 | - | ||
| 218 | -```python | ||
| 219 | -with npu_stream_switch(enable_multi_streams, "33"): | ||
| 220 | - if enable_multi_streams: | ||
| 221 | - if self.enable_aclgraph: | ||
| 222 | - tng.ops.npu_tagged_event_wait(indexer_npu_events[1]) | ||
| 223 | - else: | ||
| 224 | - tng.scope.npu_wait_tensor(x, q_b) | ||
| 225 | - weights = self.weights_proj(x.view(-1, self.dim)) | ||
| 226 | -``` | ||
| 227 | - | ||
| 228 | -DeepSeek-V3.2-Exp 同构(`models/deepseek-v3.2-exp/models/indexer.py:256` 进 `"22"`、`:283` 进 `"33"`、`wq_b`/`weights_proj` 定义于 `:85`/`:95`),唯一差异:Q/K 路径 RoPE 用 `torch_npu.npu_rotary_mul`(非 `npu_interleave_rope`)。 | ||
| 229 | - | ||
| 230 | -> ⚠️ 与案例叙述的差异(@2c45117):案例 md 把 Q 路径 RoPE 一律写作 `npu_rotary_mul`,并把 `npu_wait_tensor` 段落描述为「图模式开启」配合 tagged event。实际:(1) GLM-5 用 `npu_interleave_rope`、仅 DeepSeek-V3.2-Exp 用 `npu_rotary_mul`;(2) `npu_wait_tensor` 恰是**非 aclgraph(eager)** 分支,aclgraph 分支才走 `tagged_event_wait`,二者按 `self.enable_aclgraph` 互斥,并非「叠加」。 | ||
| 231 | - | ||
| 232 | -### 适用与风险 | ||
| 233 | - | ||
| 234 | -- 适用:decode 小 batch 前处理时延敏感、Q-Matmul 与 Weight/K 链硬件性质互补的 Prolog 类算子链;prefill 不启用(`not is_prefill`)。 | ||
| 235 | -- 风险:同步点(`npu_wait_tensor` 的 src/dst tensor、tagged-event 编号 `indexer_npu_events[0..3]` 与生命周期)设计不当会读到未就绪输入;eager 与 aclgraph 两条同步路径必须按 `enable_aclgraph` 严格二选一,混用会导致图编译失败或精度问题;Q 量化再入 `"22"` 依赖 `tagged_event_wait(events[2])` 的正确插桩。 | ||
| 236 | - | ||
| 237 | -代表实现 DeepSeek-V3.2-Exp(见 [deepseek-v3.2-exp](../models/deepseek_v3_2_exp.md)),相似实现 GLM-5(见 [glm-5](../models/glm_5.md));下游消费见 [lightning-indexer](../modules/lightning_indexer.md)。 | ||
| 238 | 170 | ||
| 239 | ## Evidence | 171 | ## Evidence |
| 240 | 172 | ||
| @@ -259,4 +191,4 @@ DeepSeek-V3.2-Exp 同构(`models/deepseek-v3.2-exp/models/indexer.py:256` 进 | |||
| 259 | - [multi-stream](../techniques/multi_stream.md)(多流方法论总纲;本页 [多流优化(case pattern)](#多流优化case-pattern) 记 Indexer Prolog 前处理切流细节) | 191 | - [multi-stream](../techniques/multi_stream.md)(多流方法论总纲;本页 [多流优化(case pattern)](#多流优化case-pattern) 记 Indexer Prolog 前处理切流细节) |
| 260 | - [glm-5](../models/glm_5.md)(GLM-5 `indexer.py` 同构多流前处理,Q 路径 RoPE 用 `npu_interleave_rope`) | 192 | - [glm-5](../models/glm_5.md)(GLM-5 `indexer.py` 同构多流前处理,Q 路径 RoPE 用 `npu_interleave_rope`) |
| 261 | - [deepseek-v4](../models/deepseek_v4.md)(V4 CSA 层 IndexerProlog 前置 LI,量化路径 Indexer Cache C8) | 193 | - [deepseek-v4](../models/deepseek_v4.md)(V4 CSA 层 IndexerProlog 前置 LI,量化路径 Indexer Cache C8) |
| 262 | -- [compressor](compressor.md)(V4 CSA 层 IndexerProlog 的 LI 作用于 Compressor 压缩后的 cmp_KV) | 194 | +- [compressor](compressor.md)(V4 CSA 层 IndexerProlog 的 LI 作用于 Compressor 压缩后的 cmp_KV) |
| @@ -1,7 +1,17 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: Lightning Indexer Kernel | 3 | title: Lightning Indexer Kernel |
| 4 | -description: '`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token 精确(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention,将 Top-k 计算融合进单一算子(相较外置收益 ~20%)。K 随模型而异:V3.2-Exp DSA 在完整上下文选 TopK=2048 喂给 SparseFlashAttention;V4 仅在 C4A(compress_ratio=4,CSA)层对 Compressor 压缩后 cmp_KV 做 Top-k(运行时取 config.index_topk,guide 例 512)喂给 SAS 的 SCFA(不同模型不同 K 并列保留)。提供 AscendC/PyPTO/TileLang 三路实现。V4 CSA 层增强:A3 LI Tiling 沿用 V3.2-Exp,适配 compress_ratio 阶梯形状 mask 产 cmp_sparse_indices;950PR/DT 量化版 LI(INT8/FP8 共用 torch.ops.custom.npu_quant_lightning_indexer,非独立 FP8 算子;FP8 区别在上游 q 经 npu_dynamic_block_quant 量化、INT8 经 npu_dynamic_quant);Top-k 两套平台分支并列:Atlas-A3 sort32+mrgsort(S_k=16384,k=512 约 28375 cycle,沿用 V3.2-Exp)/ 950PR/DT Histograms 直方图(保序变换+多级直方图分桶 value_k=a1·2^24+a2·2^16+a3·2^8+a4+向量化筛选,理论 (S_k/256·111+192) cycle,S_k=16384 约 7296 cycle,cycle 前提为 950PR/DT 平台)。阶梯 mask 形状参数源未给(待验证)。' | 4 | +description: '`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 |
| 5 | + —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token | ||
| 6 | + 精确(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention,将 Top-k 计算融合进单一算子(具体收益见平台限定的来源观测)。K 随模型而异:V3.2-Exp | ||
| 7 | + DSA 在完整上下文选 TopK=2048 喂给 SparseFlashAttention;V4 仅在 C4A(compress_ratio=4,CSA)层对 | ||
| 8 | + Compressor 压缩后 cmp_KV 做 Top-k(运行时取 config.index_topk,guide 例 512)喂给 SAS 的 SCFA(不同模型不同 | ||
| 9 | + K 并列保留)。提供 AscendC/PyPTO/TileLang 三路实现。V4 CSA 层增强:A3 LI Tiling 沿用 V3.2-Exp,适配 compress_ratio | ||
| 10 | + 阶梯形状 mask 产 cmp_sparse_indices;950PR/DT 量化版 LI(INT8/FP8 共用 torch.ops.custom.npu_quant_lightning_indexer,非独立 | ||
| 11 | + FP8 算子;FP8 区别在上游 q 经 npu_dynamic_block_quant 量化、INT8 经 npu_dynamic_quant);Top-k | ||
| 12 | + 两套平台分支并列:Atlas-A3 sort32+mrgsort(S_k=16384,k=512 约 28375 cycle,沿用 V3.2-Exp)/ 950PR/DT | ||
| 13 | + Histograms 直方图(保序变换+多级直方图分桶 value_k=a1·2^24+a2·2^16+a3·2^8+a4+向量化筛选,理论 (S_k/256·111+192) | ||
| 14 | + cycle,S_k=16384 约 7296 cycle,cycle 前提为 950PR/DT 平台)。阶梯 mask 形状参数源未给(待验证)。' | ||
| 5 | tags: | 15 | tags: |
| 6 | - task.implementation | 16 | - task.implementation |
| 7 | - topic.attention | 17 | - topic.attention |
| @@ -14,17 +24,16 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md | 24 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md |
| 15 | role: supporting | 25 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 26 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 27 | +updated_at: '2026-10-22T10:35:31Z' |
| 18 | platforms: | 28 | platforms: |
| 19 | - a3 | 29 | - a3 |
| 20 | - '950' | 30 | - '950' |
| 21 | --- | 31 | --- |
| 22 | - | ||
| 23 | # Lightning Indexer Kernel | 32 | # Lightning Indexer Kernel |
| 24 | 33 | ||
| 25 | ## Summary | 34 | ## Summary |
| 26 | 35 | ||
| 27 | -`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token **精确**(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention。当前实现**将 Top-k 计算融合进单一算子**,相较 Top-k 外置收益 ~20%。模块语义见 [lightning-indexer](../modules/lightning_indexer.md)。 | 36 | +`LightningIndexer` 是 DeepSeek-V3.2-Exp / DeepSeek-V4 稀疏 Attention 的前置算子 —— 完整执行 `BatchMatmul → ReLU → Broadcast × W → ReduceSum → Top-k` 五步,为每 query token **精确**(非近似)选出 TopK 个相关 KV index 喂给下游稀疏 Attention。当前实现**将 Top-k 计算融合进单一算子**,相关 AscendC 调优记录见[A3 来源观测](../../optimizations/benchmark_observations/ascendc_a3_li_hcpost_source_observations.md)。模块语义见 [lightning-indexer](../modules/lightning_indexer.md)。 |
| 28 | 37 | ||
| 29 | Top-k 的 **K 随模型而异**:V3.2-Exp DSA 在完整上下文上选 **TopK=2048** 喂给 [sparse-flash-attention](sparse_flash_attention.md);V4 仅在 C4A(compress_ratio=4,CSA)层、对 Compressor 压缩后的 cmp_KV 做 Top-k(V4 AscendC guide 以 **512** 为示例 K,运行时实际取 `config.index_topk`,仓内 `configuration_deepseek.py` 默认 **2048**,详见 [deepseek-v4](../models/deepseek_v4.md)),结果喂给 [sparse-attn-sharedkv](sparse_attn_sharedkv.md)(SAS)的 SCFA 路径(不同模型不同 K,并列保留)。 | 38 | Top-k 的 **K 随模型而异**:V3.2-Exp DSA 在完整上下文上选 **TopK=2048** 喂给 [sparse-flash-attention](sparse_flash_attention.md);V4 仅在 C4A(compress_ratio=4,CSA)层、对 Compressor 压缩后的 cmp_KV 做 Top-k(V4 AscendC guide 以 **512** 为示例 K,运行时实际取 `config.index_topk`,仓内 `configuration_deepseek.py` 默认 **2048**,详见 [deepseek-v4](../models/deepseek_v4.md)),结果喂给 [sparse-attn-sharedkv](sparse_attn_sharedkv.md)(SAS)的 SCFA 路径(不同模型不同 K,并列保留)。 |
| 30 | 39 | ||
| @@ -131,9 +140,9 @@ config::SetPassOption(L1_REUSE, 32); // 动态 Shape 与特定 Shape 的泛化 | |||
| 131 | TileShape::Current().SetVecTile({64, 256}); // MatMul 输出到连续内存后 Vector 大颗粒度消费 | 140 | TileShape::Current().SetVecTile({64, 256}); // MatMul 输出到连续内存后 Vector 大颗粒度消费 |
| 132 | ``` | 141 | ``` |
| 133 | 142 | ||
| 134 | -- 针对异常点 1:通过修改相应 Pass 补齐泛化能力 → **+10%**; | 143 | +- 针对异常点 1:通过修改对应 Pass 补齐泛化能力。 |
| 135 | -- 针对异常点 2:通过 `SetVecTile({64, 256})` 改成大颗粒度 Vector 运算 → **+17%**; | 144 | +- 针对异常点 2:通过 `SetVecTile({64, 256})` 改为大颗粒度 Vector 运算。 |
| 136 | -- 综合性能提升 **27%**,新版泳道图: | 145 | +- 相对结果统一见[分组来源观测](../../optimizations/benchmark_observations/operator_tuning_source_observations.md),新版泳道图仍用于解释依赖和调度: |
| 137 | 146 | ||
| 138 |  | 147 |  |
| 139 | *来源:[`docs/models/deepseek-v3.2-exp/figures/DeepseekLightningIndexer_1.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/figures/DeepseekLightningIndexer_1.png)(cann-recipes-infer @ 2f82d10)* | 148 | *来源:[`docs/models/deepseek-v3.2-exp/figures/DeepseekLightningIndexer_1.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/figures/DeepseekLightningIndexer_1.png)(cann-recipes-infer @ 2f82d10)* |
| @@ -205,7 +214,7 @@ $$value_k = a_1\times 2^{24} + a_2\times 2^{16} + a_3\times 2^{8} + a_4$$ | |||
| 205 | ## Evidence | 214 | ## Evidence |
| 206 | 215 | ||
| 207 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md)(LI 算子 Tiling、Pipeline、ReLU 随路、TopK 指令、Mat-Duce 量化、TilingKey 模板、分核下沉) | 216 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md)(LI 算子 Tiling、Pipeline、ReLU 随路、TopK 指令、Mat-Duce 量化、TilingKey 模板、分核下沉) |
| 208 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md)(PyPTO LI 调优 +27%,异常点分析) | 217 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_pypto_operator_guide.md)(PyPTO LI 调优与异常点分析) |
| 209 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_tilelang_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_tilelang_operator_guide.md)(TileLang LI Cube/Vector 分解) | 218 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_tilelang_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_tilelang_operator_guide.md)(TileLang LI Cube/Vector 分解) |
| 210 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/ascendc/src/`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/ops/ascendc/src/)(AscendC LI kernel 源码)、`ops/pypto/src/lightning_indexer_pto/`、`ops/tilelang/lightning_indexer.py` | 219 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/ascendc/src/`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/ops/ascendc/src/)(AscendC LI kernel 源码)、`ops/pypto/src/lightning_indexer_pto/`、`ops/tilelang/lightning_indexer.py` |
| 211 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md)(V4 LI 增强:compress_ratio 阶梯 mask、FP8 全量化、950PR/DT 直方图 Top-k 算法与 cycle 推导;A3 LI Tiling 沿用 V3.2-Exp)。文档由 commit `985b882`/`5e9e41d` 编辑(diff 2f82d10..2c45117) | 220 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_ascendc_operator_guide.md)(V4 LI 增强:compress_ratio 阶梯 mask、FP8 全量化、950PR/DT 直方图 Top-k 算法与 cycle 推导;A3 LI Tiling 沿用 V3.2-Exp)。文档由 commit `985b882`/`5e9e41d` 编辑(diff 2f82d10..2c45117) |
| @@ -238,4 +247,4 @@ $$value_k = a_1\times 2^{24} + a_2\times 2^{16} + a_3\times 2^{8} + a_4$$ | |||
| 238 | - [sasg](../../../training/concepts/kernels/sasg.md)(跨域·训练:V4 SAS 反向 SASG 与 LI 配合) | 247 | - [sasg](../../../training/concepts/kernels/sasg.md)(跨域·训练:V4 SAS 反向 SASG 与 LI 配合) |
| 239 | - [deepseek-v4-continue-pretrain](../../../training/guides/recipes/deepseek_v4_continue_pretrain.md)(跨域·训练:DeepSeek-V4 续训(LI 训练侧)) | 248 | - [deepseek-v4-continue-pretrain](../../../training/guides/recipes/deepseek_v4_continue_pretrain.md)(跨域·训练:DeepSeek-V4 续训(LI 训练侧)) |
| 240 | - [autofuse-train](../../../training/concepts/techniques/autofuse_train.md)(跨域·训练:V4 训练入图(inner_attention LI 跳过编译)) | 249 | - [autofuse-train](../../../training/concepts/techniques/autofuse_train.md)(跨域·训练:V4 训练入图(inner_attention LI 跳过编译)) |
| 241 | -- [smla](../../../training/concepts/kernels/smla.md)(跨域·训练:SMLA 的 LI-loss 融进 klloss_grad 反向) | 250 | +- [smla](../../../training/concepts/kernels/smla.md)(跨域·训练:SMLA 的 LI-loss 融进 klloss_grad 反向) |
| @@ -1,7 +1,11 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: HcPre / HcPost (mHC 融合算子) | 3 | title: HcPre / HcPost (mHC 融合算子) |
| 4 | -description: '`HcPre` 与 `HcPost` 是 DeepSeek-V4 mHC 残差结构的成对融合算子,完成 Attention/MoE 子层前后的多路↔单路变换。HcPre 融合 RMSNorm、hc_fn 线性变换、Sinkhorn-Knopp 和 pre 加权求和,输出 y、post、comb;HcPost 用 post 扩展子层输出并与 comb 残差合并,目标是让中间数据留在 UBuffer。A3 AscendC 采用 Cube:Vector=1:2 流水,Decode 切 K、Prefill 切 token;PyPTO 以 MPMD 并行 Vector RMSNorm 与 Cube MatMul,并用 split-K 和静态展开。hc_post 的 VF 报告提升 22.7%。Native 与 PyPTO 的首轮 Sinkhorn 归一顺序不同,应以各自代码为准。' | 4 | +description: '`HcPre` 与 `HcPost` 是 DeepSeek-V4 mHC 残差结构的成对融合算子,完成 Attention/MoE 子层前后的多路↔单路变换。HcPre |
| 5 | + 融合 RMSNorm、hc_fn 线性变换、Sinkhorn-Knopp 和 pre 加权求和,输出 y、post、comb;HcPost 用 post 扩展子层输出并与 | ||
| 6 | + comb 残差合并,目标是让中间数据留在 UBuffer。A3 AscendC 采用 Cube:Vector=1:2 流水,Decode 切 K、Prefill | ||
| 7 | + 切 token;PyPTO 以 MPMD 并行 Vector RMSNorm 与 Cube MatMul,并用 split-K 和静态展开。VF 的历史测量另见来源观测。Native | ||
| 8 | + 与 PyPTO 的首轮 Sinkhorn 均先行归一再列归一,数值实现细节以各自代码为准。' | ||
| 5 | tags: | 9 | tags: |
| 6 | - task.implementation | 10 | - task.implementation |
| 7 | - topic.model_architecture | 11 | - topic.model_architecture |
| @@ -15,12 +19,11 @@ sources: | |||
| 15 | - resource: https://gitcode.com/cann/torchtitan-npu/blob/8f128cd177e9c9a14d9b70f4491bde28bc342481/docs/feature_guides/npu_fused_ops.md | 19 | - resource: https://gitcode.com/cann/torchtitan-npu/blob/8f128cd177e9c9a14d9b70f4491bde28bc342481/docs/feature_guides/npu_fused_ops.md |
| 16 | role: supporting | 20 | role: supporting |
| 17 | created_at: '2026-05-30T00:00:00Z' | 21 | created_at: '2026-05-30T00:00:00Z' |
| 18 | -updated_at: '2026-08-27T12:23:11Z' | 22 | +updated_at: '2026-10-22T10:35:31Z' |
| 19 | platforms: | 23 | platforms: |
| 20 | - a3 | 24 | - a3 |
| 21 | - '950' | 25 | - '950' |
| 22 | --- | 26 | --- |
| 23 | - | ||
| 24 | # HcPre / HcPost (mHC 融合算子) | 27 | # HcPre / HcPost (mHC 融合算子) |
| 25 | 28 | ||
| 26 | ## Summary | 29 | ## Summary |
| @@ -58,48 +61,11 @@ Decode 场景 m(token 数)较小,故在 **K 轴上做多核切分**提升 | |||
| 58 | - **阶段一**:搬运 → 类型转换 → Cube 计算形成 CV 流水,中间内存较小、基本 L2 命中,提升流水效率。 | 61 | - **阶段一**:搬运 → 类型转换 → Cube 计算形成 CV 流水,中间内存较小、基本 L2 命中,提升流水效率。 |
| 59 | - **阶段二**:提前搬运 `x`,掩盖 `pre`、`post`、`combine` 的 Vector 计算开销。 | 62 | - **阶段二**:提前搬运 `x`,掩盖 `pre`、`post`、`combine` 的 Vector 计算开销。 |
| 60 | 63 | ||
| 61 | -**性能(源文档实测,hc=4, d=4096):** | 64 | +来源 shape 扫描统一见[A3 LI/HcPost 来源观测](../../optimizations/benchmark_observations/ascendc_a3_li_hcpost_source_observations.md);不在机制卡重复维护性能表。 |
| 62 | - | ||
| 63 | -| 规格 b·s | 性能 (us) | | ||
| 64 | -| ---- | ---- | | ||
| 65 | -| 16 | 32.8 | | ||
| 66 | -| 32 | 37 | | ||
| 67 | -| 64 | 44 | | ||
| 68 | -| 128 | 60 | | ||
| 69 | - | ||
| 70 | -### Prefill 场景模板(分 token) | ||
| 71 | - | ||
| 72 | -Prefill 场景按 **token 进行分核**形成 CV 流水。当前实现中一阶段与二阶段的 Vector 计算**串行**执行;源文档列出后续优化方向:让两个 V 核分别承担一阶段、二阶段的 Vector 计算,流水执行进一步掩盖开销。 | ||
| 73 | - | ||
| 74 | -**性能(源文档实测,hc=4, d=4096):** | ||
| 75 | - | ||
| 76 | -| 规格 b·s | 性能 (us) | | ||
| 77 | -| ---- | ---- | | ||
| 78 | -| 4K | 1254.916 | | ||
| 79 | -| 16K | 4784.204 | | ||
| 80 | -| 64K | 18545.39 | | ||
| 81 | - | ||
| 82 | -## HcPost AscendC 实现:消除内存墙 | ||
| 83 | - | ||
| 84 | -`HcPost` 在小算子下的瓶颈是内存墙;融合后 Vector 中间计算结果常驻 UBuffer(核内 buffer),减少 HBM 访存,极大提升性能。 | ||
| 85 | - | ||
| 86 | -Tiling:在 b、s、d 轴上分核,b、s 合并为 `bs` 轴,在 `(bs, d)` 上做 tile 块划分,每块大小 `(m, n)`。极小 case 多核切分时在启动开销与处理开销间权衡——每核最低搬运数据量 4KB、`n` 长度数据量不小于 1KB 且 512B 对齐。按 hc 轴 buffer 占用与流水掩盖的权衡分两类模板: | ||
| 87 | - | ||
| 88 | -1. **hc 较小**:一次性把 `residual` 的 tile 块 `(m, hc, n)` 全部搬入; | ||
| 89 | -2. **hc 泛化(UB 可能不够)**:在 `residual` 的 hc 轴上开循环流水,每次搬入 t 个。 | ||
| 90 | - | ||
| 91 | -**性能(源文档实测,hc=4, d=4096):** | ||
| 92 | - | ||
| 93 | -| 规格 b·s | 性能 (us) | | 规格 b·s | 性能 (us) | | ||
| 94 | -| ---- | ---- | --- | ---- | ---- | | ||
| 95 | -| 8 | 5.09 | | 128 | 13.85 | | ||
| 96 | -| 16 | 5.37 | | 4K | 319.86 | | ||
| 97 | -| 32 | 6.99 | | 16K | 1264.68 | | ||
| 98 | -| 64 | 9.16 | | 64K | 5044.53 | | ||
| 99 | 65 | ||
| 100 | ## PyPTO 实现(hc_pre / hc_post) | 66 | ## PyPTO 实现(hc_pre / hc_post) |
| 101 | 67 | ||
| 102 | -PyPTO 以较少代码量(`hc_pre` 约 0.4 KLOC)实现 `hc_pre`,单 die、B=64、MTP1、S2=8K 场景下实测 70 us。关键技巧(实现见 `ops/pypto_python/impl/hc_pre_pypto.py`): | 68 | +PyPTO 以较少代码量(`hc_pre` 约 0.4 KLOC)实现 `hc_pre`,该固定单 die 工作负载的测量统一见[PyPTO V4 待核验观测](../../optimizations/benchmark_observations/pypto_v4_operator_source_observations.md)。关键技巧(实现见 `ops/pypto_python/impl/hc_pre_pypto.py`): |
| 103 | 69 | ||
| 104 | - **MPMD 流水并行**:计算流程第一步是 Vector 计算(RMSNorm 分母)、第二步 `x @ hc_fn` 是 Cube 矩阵乘,二者无依赖。PyPTO 框架支持两任务同时执行,无需手动插入同步。 | 70 | - **MPMD 流水并行**:计算流程第一步是 Vector 计算(RMSNorm 分母)、第二步 `x @ hc_fn` 是 Cube 矩阵乘,二者无依赖。PyPTO 框架支持两任务同时执行,无需手动插入同步。 |
| 105 | - **指令优化(免显式广播)**:Sinkhorn-Knopp 涉及大量重复的归约串联逐元素乘除;通过 pass 优化与 PTO 指令(`set_operation_config(combine_axis=True)`)避免显式广播,实现高性能逐元素计算。 | 71 | - **指令优化(免显式广播)**:Sinkhorn-Knopp 涉及大量重复的归约串联逐元素乘除;通过 pass 优化与 PTO 指令(`set_operation_config(combine_axis=True)`)避免显式广播,实现高性能逐元素计算。 |
| @@ -107,7 +73,7 @@ PyPTO 以较少代码量(`hc_pre` 约 0.4 KLOC)实现 `hc_pre`,单 die、B | |||
| 107 | - **Prefill 转置技巧(guide/test 示例路径)**:Prefill 的 `t`(0 轴)通常较大,而 Sinkhorn-Knopp 归约轴很小(`hc_mult=4`)。采用数学等价的转置技巧,在矩阵乘中直接算 `hc_fn^T @ x^T`(代码 `hc_pre_kernel_prefill` 用 `matmul(hc_fn, x_fp32, b_trans=True)` 产 `(mix_hc, t)`),使 `t` 置于尾轴、归约轴变为 -2/-3 轴,提高计算效率。注意 `hc_pre_kernel_prefill` 在仓内仅由 `test_hc_pre_pypto.py` 调用,是 guide / test 示例路径;PyPTO 的生产入口是 `hc_pre_kernel`(见下条)。 | 73 | - **Prefill 转置技巧(guide/test 示例路径)**:Prefill 的 `t`(0 轴)通常较大,而 Sinkhorn-Knopp 归约轴很小(`hc_mult=4`)。采用数学等价的转置技巧,在矩阵乘中直接算 `hc_fn^T @ x^T`(代码 `hc_pre_kernel_prefill` 用 `matmul(hc_fn, x_fp32, b_trans=True)` 产 `(mix_hc, t)`),使 `t` 置于尾轴、归约轴变为 -2/-3 轴,提高计算效率。注意 `hc_pre_kernel_prefill` 在仓内仅由 `test_hc_pre_pypto.py` 调用,是 guide / test 示例路径;PyPTO 的生产入口是 `hc_pre_kernel`(见下条)。 |
| 108 | - **生产入口与 Unrollist 动态转静态**:PyPTO 生产入口为 `hc_pre_kernel`(`hc_pre_pypto.py:132`,被生产 wrapper 在 `:402` 调用;用 `matmul(x_fp32, hc_fn, b_trans=True)` + `enable_split_k`),其 `unroll_list=[256, 64, 16, 4, 1]` 对动态值 `t` 多分档 + 静态 Tile 切分以适应不同 `t`。示例路径 `hc_pre_kernel_prefill` 则用 `unroll_list=[128, 1]`。这一"动态值多分档 + 静态 Tile 切分"思路使 Prefill/Decode 的 `t` 差异(一套切分难以两端兼优)在各场景均达较优性能。 | 74 | - **生产入口与 Unrollist 动态转静态**:PyPTO 生产入口为 `hc_pre_kernel`(`hc_pre_pypto.py:132`,被生产 wrapper 在 `:402` 调用;用 `matmul(x_fp32, hc_fn, b_trans=True)` + `enable_split_k`),其 `unroll_list=[256, 64, 16, 4, 1]` 对动态值 `t` 多分档 + 静态 Tile 切分以适应不同 `t`。示例路径 `hc_pre_kernel_prefill` 则用 `unroll_list=[128, 1]`。这一"动态值多分档 + 静态 Tile 切分"思路使 Prefill/Decode 的 `t` 差异(一套切分难以两端兼优)在各场景均达较优性能。 |
| 109 | 75 | ||
| 110 | -`hc_post` 在仓内**只有 Native 与 AscendC 两版实现,没有 PyPTO 版**(`mhc.py` 仅注册 `hc_post`(native) 与 `hc_post_ascendc`,且 `ops/pypto_python` 内无 `hc_post` 注册;代码注释明确 "hc_post currently support Native and AscendC version")。PyPTO guide 中关于 `hc_post` 计算式 `y = post·x + sum(comb·residual)`、以及毕昇 PTO 编译器 VF 自动融合相比未使能提升 **22.7%** 的论述,描述的是 PTO-ISA / VF 融合能力本身("PTO-ISA 跨 A2/A3/950D 代际兼容、兼容不降速"),而非仓内已落地的 `hc_post` PyPTO 算子。框架语义见 [pypto](../frameworks/pypto.md)。 | 76 | +`hc_post` 在仓内**只有 Native 与 AscendC 两版实现,没有 PyPTO 版**(`mhc.py` 仅注册 `hc_post`(native) 与 `hc_post_ascendc`,且 `ops/pypto_python` 内无 `hc_post` 注册;代码注释明确 "hc_post currently support Native and AscendC version")。PyPTO guide 中关于 `hc_post` 计算式 `y = post·x + sum(comb·residual)`、以及毕昇 PTO 编译器 VF 自动融合相比未使能提升 **来源观测中的相对值** 的论述,描述的是 PTO-ISA / VF 融合能力本身("PTO-ISA 跨 A2/A3/950D 代际兼容、兼容不降速"),而非仓内已落地的 `hc_post` PyPTO 算子。框架语义见 [pypto](../frameworks/pypto.md)。 |
| 111 | 77 | ||
| 112 | > Sinkhorn-Knopp 归一化顺序(PyPTO guide L190「每次迭代先逐行归一化再逐列归一化」;mHC guide 未展开此算法):三路代码首步**一致**,均先行归一再列归一、再进入 iters-1 轮行(-1)→列(-2)交替——Native(`mhc.py:44-51`)首次行归一用 `softmax(-1)`+eps、PyPTO(`hc_pre_pypto.py:51-57`)用数值稳定 `exp(x − row_max) / row_sum`、TileLang(`hc_split_sinkhorn_kernel.py:114-119`)reduce_max→sub→exp→reduce_sum,三者数学等价且与文档「先行后列」一致(softmax(-1) 即首轮行归一,**无首步顺序差异**)。详细公式见 [mhc](../modules/mhc.md)。 | 78 | > Sinkhorn-Knopp 归一化顺序(PyPTO guide L190「每次迭代先逐行归一化再逐列归一化」;mHC guide 未展开此算法):三路代码首步**一致**,均先行归一再列归一、再进入 iters-1 轮行(-1)→列(-2)交替——Native(`mhc.py:44-51`)首次行归一用 `softmax(-1)`+eps、PyPTO(`hc_pre_pypto.py:51-57`)用数值稳定 `exp(x − row_max) / row_sum`、TileLang(`hc_split_sinkhorn_kernel.py:114-119`)reduce_max→sub→exp→reduce_sum,三者数学等价且与文档「先行后列」一致(softmax(-1) 即首轮行归一,**无首步顺序差异**)。详细公式见 [mhc](../modules/mhc.md)。 |
| 113 | 79 | ||
| @@ -140,7 +106,7 @@ mHC `HcPre` / `HcPost` 的分核**基于 shape 即可确定(非值依赖)** | |||
| 140 | ## Evidence | 106 | ## Evidence |
| 141 | 107 | ||
| 142 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_mHC_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_mHC_guide.md)(frontmatter 主源:HcPre/HcPost 融合范围与价值、A3 分离架构 CV 流水、Decode 切 K / Prefill 分 token 双模板、Tiling 与流水、性能 us 表) | 108 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_mHC_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_mHC_guide.md)(frontmatter 主源:HcPre/HcPost 融合范围与价值、A3 分离架构 CV 流水、Decode 切 K / Prefill 分 token 双模板、Tiling 与流水、性能 us 表) |
| 143 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md)(hc_pre MPMD 流水、免广播指令优化、Decode 切 K、Prefill 转置技巧、Unrollist;hc_post VF 自动融合 +22.7%;hc_pre 实测 70 us / 0.4 KLOC) | 109 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_pypto_operator_guide.md)(hc_pre MPMD 流水、免广播指令优化、Decode 切 K、Prefill 转置技巧、Unrollist;hc_post VF 自动融合 +来源观测中的相对值;hc_pre 实测 70 us / 0.4 KLOC) |
| 144 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/deepseek-v4/models/modules/op_impls/mhc.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/deepseek-v4/models/modules/op_impls/mhc.py)(`hc_pre` 三注册键 `native`/`hc_pre_ascendc`(`npu_hc_pre`)/`hc_pre_pypto_a3`;`hc_post` 两注册键 `native`/`hc_post_ascendc`(`npu_hc_post`);接口产出 `(y, post, comb)` / `y`) | 110 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/deepseek-v4/models/modules/op_impls/mhc.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/deepseek-v4/models/modules/op_impls/mhc.py)(`hc_pre` 三注册键 `native`/`hc_pre_ascendc`(`npu_hc_pre`)/`hc_pre_pypto_a3`;`hc_post` 两注册键 `native`/`hc_post_ascendc`(`npu_hc_post`);接口产出 `(y, post, comb)` / `y`) |
| 145 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/pypto_python/impl/hc_pre_pypto.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/pypto_python/impl/hc_pre_pypto.py)(生产入口 `hc_pre_kernel`(`:132`,生产 wrapper 在 `:402` 调用)`matmul(x_fp32, hc_fn, b_trans=True)` + `enable_split_k` + `unroll_list=[256,64,16,4,1]`;`hc_pre_kernel_prefill`(`:249`)`matmul(hc_fn, x_fp32, b_trans=True)` 转置 + `unroll_list=[128,1]`,仅由 `example/test_hc_pre_pypto.py:224` 调用(guide/test 示例路径);`ops/pypto_python` 内无 `hc_post` 注册;`set_operation_config(combine_axis=True)`;`hc_split_sinkhorn` 首步行归一→列归一) | 111 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`ops/pypto_python/impl/hc_pre_pypto.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/ops/pypto_python/impl/hc_pre_pypto.py)(生产入口 `hc_pre_kernel`(`:132`,生产 wrapper 在 `:402` 调用)`matmul(x_fp32, hc_fn, b_trans=True)` + `enable_split_k` + `unroll_list=[256,64,16,4,1]`;`hc_pre_kernel_prefill`(`:249`)`matmul(hc_fn, x_fp32, b_trans=True)` 转置 + `unroll_list=[128,1]`,仅由 `example/test_hc_pre_pypto.py:224` 调用(guide/test 示例路径);`ops/pypto_python` 内无 `hc_post` 注册;`set_operation_config(combine_axis=True)`;`hc_split_sinkhorn` 首步行归一→列归一) |
| 146 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/deepseek-v4/models/modeling_deepseek.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/deepseek-v4/models/modeling_deepseek.py)(`OpKernel.hc_pre`/`hc_post` 每层两轮调用 `hc_attn_*`/`hc_ffn_*`;`hc_head` 单独的 Pre 风格收敛函数 L1478-1483;进网络 `unsqueeze(2).repeat(1,1,hc_mult,1)`) | 112 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/deepseek-v4/models/modeling_deepseek.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/deepseek-v4/models/modeling_deepseek.py)(`OpKernel.hc_pre`/`hc_post` 每层两轮调用 `hc_attn_*`/`hc_ffn_*`;`hc_head` 单独的 Pre 风格收敛函数 L1478-1483;进网络 `unsqueeze(2).repeat(1,1,hc_mult,1)`) |
| @@ -158,4 +124,4 @@ mHC `HcPre` / `HcPost` 的分核**基于 shape 即可确定(非值依赖)** | |||
| 158 | 124 | ||
| 159 | 运行于 Atlas A3(arch_support 已覆盖 A3 / ascend-950)。 | 125 | 运行于 Atlas A3(arch_support 已覆盖 A3 / ascend-950)。 |
| 160 | - [deepseek-v4-continue-pretrain](../../../training/guides/recipes/deepseek_v4_continue_pretrain.md)(跨域·训练:mHC 在 V4 训练侧(AutoFuse 融合小算子)) | 126 | - [deepseek-v4-continue-pretrain](../../../training/guides/recipes/deepseek_v4_continue_pretrain.md)(跨域·训练:mHC 在 V4 训练侧(AutoFuse 融合小算子)) |
| 161 | -- [mhc-train](../../../training/concepts/kernels/mhc_train.md)(跨域·训练:MindSpeed-LLM 的 mHC 训练正反向 Triton BMM + Sinkhorn 算子,与本页 AscendC 正向算子对照) | 127 | +- [mhc-train](../../../training/concepts/kernels/mhc_train.md)(跨域·训练:MindSpeed-LLM 的 mHC 训练正反向 Triton BMM + Sinkhorn 算子,与本页 AscendC 正向算子对照) |
| @@ -1,7 +1,11 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: Sparse Flash Attention Kernel | 3 | title: Sparse Flash Attention Kernel |
| 4 | -description: '`SparseFlashAttention` 是 DeepSeek-V3.2-Exp **DSA** 的主 Attention 算子 —— 在 lightning-indexer 产出的 TopK index 基础上,`Gather` 出稀疏 KV 子集并执行 Flash Attention 计算。沿用 FlashAttention 的 `C_1 (Q@K^T) → V_1 (online softmax) → C_2 (P@V) → V_2 (rescale)` 四阶段流程,但面向**离散访存**进行了专门优化。模块语义见 sparse-flash-attention。 cann-recipes-infer 提供 AscendC + TileLang 两路实现;PyPTO 侧则作为 deepseek-indexer-attention 大融合算子内的子流程出现。' | 4 | +description: '`SparseFlashAttention` 是 DeepSeek-V3.2-Exp **DSA** 的主 Attention 算子 —— |
| 5 | + 在 lightning-indexer 产出的 TopK index 基础上,`Gather` 出稀疏 KV 子集并执行 Flash Attention 计算。沿用 | ||
| 6 | + FlashAttention 的 `C_1 (Q@K^T) → V_1 (online softmax) → C_2 (P@V) → V_2 (rescale)` | ||
| 7 | + 四阶段流程,但面向**离散访存**进行了专门优化。模块语义见 sparse-flash-attention。 cann-recipes-infer 提供 AscendC | ||
| 8 | + + TileLang 两路实现;PyPTO 侧则作为 deepseek-indexer-attention 大融合算子内的子流程出现。' | ||
| 5 | tags: | 9 | tags: |
| 6 | - task.implementation | 10 | - task.implementation |
| 7 | - topic.attention | 11 | - topic.attention |
| @@ -14,11 +18,10 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md | 18 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/deepseek_v3.2_exp_ascendc_operator_guide.md |
| 15 | role: supporting | 19 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 20 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 21 | +updated_at: '2026-10-22T10:35:31Z' |
| 18 | platforms: | 22 | platforms: |
| 19 | -- agnostic | 23 | +- a3 |
| 20 | --- | 24 | --- |
| 21 | - | ||
| 22 | # Sparse Flash Attention Kernel | 25 | # Sparse Flash Attention Kernel |
| 23 | 26 | ||
| 24 | ## Summary | 27 | ## Summary |
| @@ -58,7 +61,9 @@ KV C8 量化路径改走 `torch_npu.npu_kv_quant_sparse_flash_attention`(`mode | |||
| 58 | 61 | ||
| 59 | ## Performance Signals | 62 | ## Performance Signals |
| 60 | 63 | ||
| 61 | -### AscendC 实现 | 64 | +### AscendC 实现(固定来源的 Atlas A3) |
| 65 | + | ||
| 66 | +以下 L1/L0 划分、Tiling 与 Cube/Vector 流水均来自 A3 优化报告,不作为其他硬件的配置或收益保证。 | ||
| 62 | 67 | ||
| 63 | **算子 Tiling**(基本块 `(256, 512)`): | 68 | **算子 Tiling**(基本块 `(256, 512)`): |
| 64 | 69 | ||
| @@ -72,7 +77,7 @@ KV C8 量化路径改走 `torch_npu.npu_kv_quant_sparse_flash_attention`(`mode | |||
| 72 |  | 77 |  |
| 73 | *来源:[`docs/models/deepseek-v3.2-exp/figures/pipeline.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/figures/pipeline.png)(cann-recipes-infer @ 2f82d10)* | 78 | *来源:[`docs/models/deepseek-v3.2-exp/figures/pipeline.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-v3.2-exp/figures/pipeline.png)(cann-recipes-infer @ 2f82d10)* |
| 74 | 79 | ||
| 75 | -### 访存优化(离散访存专项) | 80 | +### 访存优化(A3 AscendC 离散访存实现) |
| 76 | 81 | ||
| 77 | **问题**:连续 Attention 的访存瓶颈是数据搬运本身;Sparse Attention 单次访存量小,**单次搬运耗时甚至低于指令下发耗时** —— 指令下发阻塞反而成为新的瓶颈。 | 82 | **问题**:连续 Attention 的访存瓶颈是数据搬运本身;Sparse Attention 单次访存量小,**单次搬运耗时甚至低于指令下发耗时** —— 指令下发阻塞反而成为新的瓶颈。 |
| 78 | 83 | ||
| @@ -118,7 +123,7 @@ Cube/Vector 协同通过 `T.set_cross_flag / T.wait_cross_flag` 实现。 | |||
| 118 | 123 | ||
| 119 | ### 相对 MTP 的表现 | 124 | ### 相对 MTP 的表现 |
| 120 | 125 | ||
| 121 | -DSA 下 **MTP 对 SFA 难以加速** —— 每个 q token 单独选 TopK,MTP1 下 KV 搬运量翻倍。源文档在"MTP"章节给出对比表,SFA 的 KV 搬运 4.5 MB → 9 MB、Cube 计算 0.60 → 1.21 GFlops。见 [sparse-flash-attention](../modules/sparse_flash_attention.md)。 | 126 | +搬运与计算量对照见[DSA 模块分析案例](../../optimizations/cases/mtp_benefit_sparse_attention_and_latency_metric.md)。它不是独立测量的整网收益,不能推出所有 SFA 场景均无收益。 |
| 122 | 127 | ||
| 123 | ### Offload 协作 | 128 | ### Offload 协作 |
| 124 | 129 | ||
| @@ -148,4 +153,4 @@ DSA 下 **MTP 对 SFA 难以加速** —— 每个 q token 单独选 TopK,MTP1 | |||
| 148 | - [sfag](../../../training/concepts/kernels/sfag.md)(跨域·训练:SFA 的训练反向算子 SFAG) | 153 | - [sfag](../../../training/concepts/kernels/sfag.md)(跨域·训练:SFA 的训练反向算子 SFAG) |
| 149 | - [slig](../../../training/concepts/kernels/slig.md)(跨域·训练:Indexer 反向 SLIG(用 SFA softmax_max/sum)) | 154 | - [slig](../../../training/concepts/kernels/slig.md)(跨域·训练:Indexer 反向 SLIG(用 SFA softmax_max/sum)) |
| 150 | - [custom-cp-dsa](../../../training/concepts/parallelism/custom_cp_dsa.md)(跨域·训练:DSA 自定义 CP) | 155 | - [custom-cp-dsa](../../../training/concepts/parallelism/custom_cp_dsa.md)(跨域·训练:DSA 自定义 CP) |
| 151 | -- [deepseek-v32-pretrain](../../../training/guides/recipes/deepseek_v32_pretrain.md)(跨域·训练:DeepSeek-V3.2 32K 预训练) | 156 | +- [deepseek-v32-pretrain](../../../training/guides/recipes/deepseek_v32_pretrain.md)(跨域·训练:DeepSeek-V3.2 32K 预训练) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: DeepSeek-R1 | 3 | title: DeepSeek-R1 |
| 4 | -description: DeepSeek-R1 是 2025 年 DeepSeek 开源的大语言模型,结构沿用 DeepSeek-V3 —— MLA (Multi-head Latent Attention) + DeepSeekMoE(1 个 shared expert + 256 个 routed expert,top-k=8)。在 cann-recipes-infer 中基于 DeepSeek HuggingFace modeling_deepseek.py 迁移到昇腾 Atlas A3,通过自研 executor/ 推理框架(非 SGLang/vLLM)承载,与 Kimi-K2 / kimi-k2-thinking 共享同一套 models/deepseek_r1/models/modeling_deepseek.py 代码路径(Kimi-K2-Thinking 的 runner_deepseek.py 直接复用)。 | 4 | +description: DeepSeek-R1 的 MLA、共享与路由专家及模型结构;部署流程和性能数据分别引用规范入口。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,26 +14,20 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-r1/deepseek_r1_prefill_optimization.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-r1/deepseek_r1_prefill_optimization.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 16 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 17 | +updated_at: '2026-10-21T10:02:02Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a3 | 19 | - a3 |
| 20 | --- | 20 | --- |
| 21 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 22 | # DeepSeek-R1 | 24 | # DeepSeek-R1 |
| 23 | 25 | ||
| 24 | ## Summary | 26 | ## Summary |
| 25 | 27 | ||
| 26 | -DeepSeek-R1 是 2025 年 DeepSeek 开源的大语言模型,结构沿用 DeepSeek-V3 —— MLA (Multi-head Latent Attention) + DeepSeekMoE(1 个 shared expert + 256 个 routed expert,top-k=8)。在 cann-recipes-infer 中基于 DeepSeek HuggingFace [modeling_deepseek.py](https://huggingface.co/deepseek-ai/DeepSeek-V3-Base/blob/afb92e1fa402c2be2a9eb085312bb02e0384d6c7/modeling_deepseek.py) 迁移到昇腾 Atlas A3,通过自研 `executor/` 推理框架(非 SGLang/vLLM)承载,与 Kimi-K2 / [kimi-k2-thinking](kimi_k2_thinking.md) 共享同一套 `models/deepseek_r1/models/modeling_deepseek.py` 代码路径(Kimi-K2-Thinking 的 `runner_deepseek.py` 直接复用)。 | 28 | +DeepSeek-R1 沿用 DeepSeek-V3 的 MLA + DeepSeekMoE 架构:低秩 KV 压缩、1个共享专家与256个路由专家、top-k=8。 |
| 27 | 29 | ||
| 28 | -本实践覆盖 Prefill 与 Decode 两阶段的端到端优化:Prefill 采用 MLA-DP/SP-TP-SP 混合切分、MoE Double-Routing、micro-batch 双流、weightNZ、TND/NTD 布局优化;Decode 采用大 EP(128EP)部署、MLA 融合算子(`npu_mla_prolog_v3` + `npu_fused_infer_attention_score_v2`)、MoE 融合算子、torchair 图模式、SuperKernel、MTP1 投机推理。 | 30 | +本卡保留网络结构及其状态机制。R1 与 Kimi-K2-Thinking 的架构思路同源,但固定仓中的执行器与建模是不同文件/副本,不再表述为共用同一 modeling_deepseek.py。平台配置、开关和验收步骤见[模型部署流程](../../guides/models/deepseek_r1_deployment.md),此处只保留模型机制。 |
| 29 | - | ||
| 30 | -### Highlights | ||
| 31 | - | ||
| 32 | -- **Prefill**:MLA-DP32 + MoE-EP32 + micro-batch 在 Atlas A3 2 机 16 卡环境下,64 × 4K 输入、perfect eplb 条件下达到 **6522 tokens/卡/s**,相比无 micro-batch 提升 25%。 | ||
| 33 | -- **Decode**:W8A8C8 量化、128 卡、4K seq、global batch 6144、MTP1(平均 accept 0.7 token/draft)、perfect eplb 条件下 TPOT **44.9 ms**、吞吐 **2138 tokens/p/s**。 | ||
| 34 | -- **关键算子**:`npu_mla_prolog_v3` 融合 MLA 前置 Q/K/V Linear + RMSNorm + RoPE + KVCache 写入;`npu_fused_infer_attention_score_v2` 融合 FA 计算路径;`npu_moe_init_routing_v2` / `npu_moe_re_routing` / `npu_moe_finalize_routing` 覆盖 MoE 排序与 finalize;`npu_grouped_matmul` 覆盖 Grouped FFN。 | ||
| 35 | -- **量化**:支持 BF16 / W8A8C16 / **W8A8C8**(仅 R1 支持,Kimi-K2 不支持 C8)。见 [w8a8c8](../quantization/w8a8c8.md)。 | ||
| 36 | -- **新增性能特性**:[double-routing](../techniques/double_routing.md)、[prefill-micro-batch](../techniques/prefill_micro_batch.md)、[sp-tp-sp](../parallelism/sp_tp_sp.md)、[weight-nz](../techniques/weight_nz.md)、[superkernel](../techniques/superkernel.md)、[mtp-speculative-decoding](../techniques/mtp_speculative_decoding.md)。 | ||
| 37 | 31 | ||
| 38 | ## Architecture And Serving Traits | 32 | ## Architecture And Serving Traits |
| 39 | 33 | ||
| @@ -63,207 +57,6 @@ flowchart TD | |||
| 63 | - **gate** 输出 `topk_idx` + `topk_weight`;在 Decode 使用 `npu_moe_gating_top_k` 融合算子。 | 57 | - **gate** 输出 `topk_idx` + `topk_weight`;在 Decode 使用 `npu_moe_gating_top_k` 融合算子。 |
| 64 | - EP 部署下每卡分到 `256 / ep_size` 个 routed expert + 同时存放一个 shared expert 的完整副本。 | 58 | - EP 部署下每卡分到 `256 / ep_size` 个 routed expert + 同时存放一个 shared expert 的完整副本。 |
| 65 | 59 | ||
| 66 | -### 支持的规格 | ||
| 67 | - | ||
| 68 | -根据 `config/*.yaml` 实际提供的部署方案: | ||
| 69 | - | ||
| 70 | -| 阶段 | 配置文件 | world_size | 并行 | 量化 | 备注 | | ||
| 71 | -| ---- | ------- | ---------- | ---- | ---- | ---- | | ||
| 72 | -| Prefill | `prefill_r1_rank_32_32dp_32ep_a8w8.yaml` | 32 (2 机) | MLA-DP32 + MoE-EP32 | W8A8 | `micro_batch_mode: 1`(DP+EP),`moe_chunk_max_len: 65536` | | ||
| 73 | -| Decode | `decode_r1_rank_16_16ep_a8w8.yaml` | 16 (1 机) | MLA-DP16 + MoE-EP16 | W8A8 | `npugraph_ex`(ACL Graph 图模式),无 MTP,小规模/调试用 | | ||
| 74 | -| Decode | `decode_r1_rank_16_16tp_a8w8.yaml` | 16 (1 机) | MLA-TP16 + MoE-TP16 | W8A8 | `moe_tp_size > 1`,走 `moe_infer_tp` 路径 | | ||
| 75 | -| Decode | `decode_r1_rank_128_128ep_a8w8.yaml` | 128 (16 机) | MLA-DP128 + MoE-EP128 | W8A8 | `ge_graph`,大 EP 部署 | | ||
| 76 | -| Decode | `decode_r1_rank_128_128ep_a8w8c8_mtp.yaml` | 128 | DP128 + EP128 | **W8A8C8** | `next_n: 1`(MTP1)+ SuperKernel | | ||
| 77 | -| Decode | `decode_r1_rank_128_128ep_a8w8c8_mtp_benchmark.yaml` | 128 | DP128 + EP128 | W8A8C8 | Benchmark 配置,`force_eplb: True`、`prefill_mini_batch: 1` | | ||
| 78 | -| Decode | `decode_r1_rank_128_128ep_a8w8c8_mtp_longseq.yaml` | 128 | DP128 + EP128 | W8A8C8 | 长序列专用 | | ||
| 79 | - | ||
| 80 | -> 注:`attn_tp_size / dense_tp_size / moe_tp_size = 1` 表示 MLA 与 MoE 均以 DP 方式部署每卡完整权重;`embed_tp_size / lmhead_tp_size` 控制 embedding / lm_head 的 TP 切分。 | ||
| 81 | - | ||
| 82 | -### 权重类型 | ||
| 83 | - | ||
| 84 | -通过 `utils/weight_convert.sh` 离线转换: | ||
| 85 | - | ||
| 86 | -```bash | ||
| 87 | -# BF16(R1/K2) | ||
| 88 | -bash utils/weight_convert.sh --input_fp8_hf_path ... --output_hf_path ... --quant_mode bfloat16 | ||
| 89 | -# W8A8C16(R1/K2) | ||
| 90 | -bash utils/weight_convert.sh ... --quant_mode w8a8c16 | ||
| 91 | -# W8A8C8(仅 R1) | ||
| 92 | -bash utils/weight_convert.sh ... --quant_mode w8a8c8 | ||
| 93 | -``` | ||
| 94 | - | ||
| 95 | -## Known Optimizations | ||
| 96 | - | ||
| 97 | -### Prefill 阶段 | ||
| 98 | - | ||
| 99 | -以下四大块优化组合使用,综合提升 prefill 吞吐。 | ||
| 100 | - | ||
| 101 | -#### MLA 部署策略 | ||
| 102 | - | ||
| 103 | -Prefill 的 MLA 部分可选两种策略,按请求分布与序列长度决定: | ||
| 104 | - | ||
| 105 | -- **纯 DP 策略**:每卡部署完整模型权重,不做切分。KV-Cache 按请求分卡、无冗余;MLA 内无通信算子,易于用满算力。**适合**请求量大 / KV-Cache 占用高的高吞吐场景。缺点:短序列混合批次下计算负载不均;请求数 < 卡数时 TTFT 被拉长。 | ||
| 106 | -- **SP-TP-SP 混合切分**:把 batch 与 seq_len 合轴后按卡数 SP 切 token,FA 内切 head(TP),完成后 AllToAll 转回 SP 给 o_proj。**适合**单请求长序列或请求数少/分布不均、对 TTFT 敏感的场景。引入 AllGather/AllToAll 通信,且每卡存完整 KV-Cache。详见 [sp-tp-sp](../parallelism/sp_tp_sp.md)。 | ||
| 107 | - | ||
| 108 | -判断原则来自源文档:"当请求量较大或 KVCache 内存占用较高时,可以考虑采用 DP 切分策略,以提升吞吐量;当请求数量较少、分布不均匀或单次请求序列长度较长时,可以考虑采用 SP-TP-SP 混合切分策略,以降低时延。" | ||
| 109 | - | ||
| 110 | -#### MoE Double-Routing (EP) | ||
| 111 | - | ||
| 112 | -MoE 阶段走 EP,路由专家均匀分布到每张卡,同时每卡同步存放 shared expert。采用 **Double-Routing**:每卡完成 2 次 init-routing + 2 次 finalize-routing,通过 `tokens_per_expert` 的 AllToAll+ReduceSum 推导 `input_splits` / `output_splits`,再执行 token AllToAll。完整 8 步流程与代码见 [double-routing](../techniques/double_routing.md)。 | ||
| 113 | - | ||
| 114 | -相对 AllGather 方案: | ||
| 115 | - | ||
| 116 | -1. 负载均衡时峰值显存更低(无数据冗余); | ||
| 117 | -2. 通信量更低,AllToAll 耗时更优。 | ||
| 118 | - | ||
| 119 | -#### TND / NTD 布局 | ||
| 120 | - | ||
| 121 | -常规 FA 用 BNSD `[batch, num_head, seq_len, head_dim]`,但 Prefill 输入长度不定时必须 padding 导致浪费。TND `[batch*seq_len, num_head, head_dim]` 合轴消除 padding,但 FA 计算时数据访问不连续、带宽效率低,且需要 transpose 额外开销。 | ||
| 122 | - | ||
| 123 | -实际 **MLA 阶段 FA 采用 NTD 布局**(`[num_head, batch*seq_len, head_dim]` 形变等价),带宽更友好,同时配合 matmul 权重拆分 + FA 支持独立的 NoPE/RoPE 输入,可以完全消除 split/concat/transpose 开销。**实测 FA 效率相对 TND 提升 15%**。源代码在不同 FA 调用点用两种布局串表达该约定:`input_layout="NTD_TND"`(`modeling_deepseek.py:1038`,即下图所示的 Attention NTD 排布)与 `input_layout="TND_NTD"`(`:1164/:1284`),分别对应不同 FA 调用阶段的数据排布——二者并存、非矛盾(下图展示 `NTD_TND` 一例)。 | ||
| 124 | - | ||
| 125 | -Attention 阶段 NTD 排布的数据布局原貌如下: | ||
| 126 | - | ||
| 127 | - | ||
| 128 | -*来源:[`docs/models/deepseek-r1/figures/image_attention_ntd_format.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/deepseek-r1/figures/image_attention_ntd_format.png)(cann-recipes-infer @ 2f82d10)* | ||
| 129 | - | ||
| 130 | -#### Micro-batch 双流 | ||
| 131 | - | ||
| 132 | -Prefill 属于 compute-bound + 30% 通信占比场景。将输入拆成 2 个 micro-batch,主/副 NPU stream 上交错排布 **atten / init_routing / dispatch / mlp / combine / finalize_routing** 6 个阶段,使 dispatch 与 combine 的 AllToAll 完全被计算掩盖。开启后 A3 2 机 16 卡 64×4K 输入性能对比: | ||
| 133 | - | ||
| 134 | -| 切分策略 | 时延 (ms) | 总吞吐 (tokens/s) | 平均吞吐 (tokens/卡/s) | | ||
| 135 | -| ---- | ---- | ---- | ---- | | ||
| 136 | -| MLA-DP32 + MoE-EP32 | 3128 | 83805 | 5237 | | ||
| 137 | -| + micro-batch | 2512 | 104356 | **6522** | | ||
| 138 | - | ||
| 139 | -约束:计算和通信线性度要好;非 host-bound;通信必须走默认 SDMA 模式(`HCCL_OP_EXPANSION_MODE` 若为 AIV 会与计算争硬件资源,反而变慢)。详见 [prefill-micro-batch](../techniques/prefill_micro_batch.md)。 | ||
| 140 | - | ||
| 141 | -由 `micro_batch_mode` 控制(0: 关;1: DP+EP;2: SP+TP+EP)。 | ||
| 142 | - | ||
| 143 | -#### weightNZ 格式 | ||
| 144 | - | ||
| 145 | -所有 matmul 权重转换为昇腾亲和的 **weightNZ**(512B 对齐),显著提升带宽访问效率。例如 int8 [16384, 7168] 转为 NZ 布局 [224, 1024, 16, 32]。见 [weight-nz](../techniques/weight_nz.md)。由 YAML `enable_weight_nz: True` 使能。 | ||
| 146 | - | ||
| 147 | -### Decode 阶段 | ||
| 148 | - | ||
| 149 | -#### 通用优化 | ||
| 150 | - | ||
| 151 | -非 MoE 部分与 Llama 类似:固定 KV Cache 大小、cos/sin 优化、AddRMSNorm 融合、全量优化 LM Head 计算量等。 | ||
| 152 | - | ||
| 153 | -#### MLA 低秩压缩 + 融合算子 | ||
| 154 | - | ||
| 155 | -参考 [DeepSeek 论文](https://arxiv.org/pdf/2405.04434) 的低秩压缩方法以减少 KVCache 内存。核心是两个融合算子: | ||
| 156 | - | ||
| 157 | -- **`npu_mla_prolog_v2` / `v3`**:替换 Attention 计算前的 Q/K/V Linear、RoPE、RmsNorm、KVCache 更新。`forward_page_attention_mla_prolog` 函数,`enable_mla_prolog: True` 使能。见 [mla-prolog](../kernels/mla_prolog.md)。 | ||
| 158 | -- **`npu_fused_infer_attention_score_v2`**:加速 MLA 的 FA 主体计算,`input_layout="TND_NTD"`。见 [fused-infer-attention-score](../kernels/fused_infer_attention_score.md)。 | ||
| 159 | - | ||
| 160 | -关键代码片段(`models/deepseek_r1/models/modeling_deepseek.py:L1216`): | ||
| 161 | - | ||
| 162 | -```python | ||
| 163 | -q_nope, q_pe, dequant_scale_q_nope, _, _ = torch.ops.npu.npu_mla_prolog_v3( | ||
| 164 | - token_x=hidden_states_int8 if enable_mm_quant_a8w8 else hidden_states, | ||
| 165 | - weight_dq=self.q_a_proj.weight, weight_uq_qr=self.q_b_proj.weight, | ||
| 166 | - weight_uk=self.kv_b_proj_w_k, weight_dkv_kr=self.kv_a_proj_with_mqa.weight, | ||
| 167 | - rmsnorm_gamma_cq=self.q_a_layernorm.weight, | ||
| 168 | - rmsnorm_gamma_ckv=self.kv_a_layernorm.weight, | ||
| 169 | - rope_sin=sin.squeeze(1), rope_cos=cos.squeeze(1), | ||
| 170 | - cache_index=cache_index, kv_cache=nope_cache, kr_cache=rope_cache, | ||
| 171 | - # ... | ||
| 172 | - cache_mode="PA_NZ", | ||
| 173 | - weight_quant_mode=2 if enable_mm_quant_a8w8 else 0, # 0/1/2/3 | ||
| 174 | - kv_cache_quant_mode=1 if self.kv_cache_quant_mode == "int8" else 0, # per-tensor int8 | ||
| 175 | - query_quant_mode=1 if enable_mm_quant_a8w8 and self.kv_cache_quant_mode == "int8" else 0, | ||
| 176 | -) | ||
| 177 | -``` | ||
| 178 | - | ||
| 179 | -`weight_quant_mode` / `kv_cache_quant_mode` / `query_quant_mode` 共同覆盖 BF16、W8A8、W8A8C8 三种配置。 | ||
| 180 | - | ||
| 181 | -#### MoE EP + 融合算子 | ||
| 182 | - | ||
| 183 | -原始 for-loop 遍历 `expert_num` 个 FFN 效率低,CANN 提供一系列融合算子: | ||
| 184 | - | ||
| 185 | -- **Router**:`torch_npu.npu_moe_gating_top_k` — 对 router 结果排序并取 top-k。 | ||
| 186 | -- **专家计算**:`torch_npu.npu_grouped_matmul` — 同时处理多个专家的计算,A8 路径额外传入 `pertoken_scale`。 | ||
| 187 | -- **多卡通信**:`torch_npu.npu_moe_distribute_dispatch_v2` / `npu_moe_distribute_combine_v2` — EP 并行下多卡间 dispatch/combine(一体化融合算子,见 [moe-dispatch-combine-v2](../techniques/moe_dispatch_combine_v2.md))。需注意 `HCCL_BUFFSIZE` 等环境变量。 | ||
| 188 | - | ||
| 189 | -#### MLP 线性层合并 | ||
| 190 | - | ||
| 191 | -原始 `DeepseekV3MLP` 有 `gate_proj` / `up_proj` / `down_proj` 三个 matmul,**合并 `gate_up_proj` = cat(gate_proj, up_proj)** 单次 matmul 提升整体计算效率。shared expert 的 `gate_up_proj` 在多流路径上与 dispatch AllToAll 并行。 | ||
| 192 | - | ||
| 193 | -#### TorchAir 图模式 | ||
| 194 | - | ||
| 195 | -Decode 时延敏感,eager 下 host 下发常成为瓶颈。TorchAir 提供昇腾亲和的 `torch.compile` 后端: | ||
| 196 | - | ||
| 197 | -- `exe_mode: "ge_graph"` → **max-autotune**(GE 图模式,最激进优化); | ||
| 198 | -- `exe_mode: "npugraph_ex"` → **reduce-overhead**(ACL Graph 模式,开销更低)。 | ||
| 199 | - | ||
| 200 | -YAML `exe_mode` 切换。详见 [torchair](../frameworks/torchair.md)。 | ||
| 201 | - | ||
| 202 | -#### MTP | ||
| 203 | - | ||
| 204 | -`next_n` 参数使能 MTP。实现细节与 GDN 类模型不同——R1 的 MLA 端 MTP 主要通过 `sparse_mode` 切换 FA 的 mask 行为(`q_len > 1` 时 `sparse_mode = 3`),KV 按 `speculative_num_draft_tokens` 布局。详见 [mtp-speculative-decoding](../techniques/mtp_speculative_decoding.md)。 | ||
| 205 | - | ||
| 206 | -#### 集合通信 AIV 展开 | ||
| 207 | - | ||
| 208 | -Decode 阶段 MoE AllToAll 量小,可以借 Vector Core 加速通信: | ||
| 209 | - | ||
| 210 | -```shell | ||
| 211 | -export HCCL_OP_EXPANSION_MODE=AIV | ||
| 212 | -``` | ||
| 213 | - | ||
| 214 | -> 注意:此开关与 Prefill micro-batch 策略冲突(AIV 占用计算资源会破坏计算/通信重叠),不可同开。 | ||
| 215 | - | ||
| 216 | -#### SuperKernel | ||
| 217 | - | ||
| 218 | -在 `ge_graph` 图模式下支持按用户范围将计算子图融合为 SuperKernel,降低任务调度等待和开销。示例整个 decode layer 全部进入一个 SuperKernel scope: | ||
| 219 | - | ||
| 220 | -```python | ||
| 221 | -from executor.utils import superkernel_scope | ||
| 222 | - | ||
| 223 | -switch = enable_superkernel | ||
| 224 | -scope = f"scope_name" | ||
| 225 | -options = f"scope_options" | ||
| 226 | -with superkernel_scope(switch, scope, options): | ||
| 227 | - your modules | ||
| 228 | -``` | ||
| 229 | - | ||
| 230 | -YAML `enable_superkernel: True`。详见 [superkernel](../techniques/superkernel.md)。 | ||
| 231 | - | ||
| 232 | -### 长序列 OOM 处理 | ||
| 233 | - | ||
| 234 | -Prefill 峰值显存主要来自 Attention softmax 中间(`batch_size * num_head * q_s * kv_s * 6 Bytes`)与 MoE 负载不均时 grouped_matmul 的临时内存。缓解手段: | ||
| 235 | - | ||
| 236 | -1. **Paged Attention + FA**:切块计算 Attention,避免 `q_s * kv_s` 峰值,YAML `enable_pa: True` + `pa_block_size: 128`。R1 的 MLA `nope_cache` / `rope_cache` 以 `FullAttention` 类型纳入框架级 [kv-cache-manager](../frameworks/kv_cache_manager.md) 的分块管理(在线按剩余显存预算折算 block 数、`is_mla_backend=True` 用于 PD 单 rank 传输)。 | ||
| 237 | -2. **prefill_mini_batch 串行**:当 decode global batch 大到 prefill OOM 时,`prefill_mini_batch > 1` 多次小 batch 串行。 | ||
| 238 | -3. **Chunk MoE**:MoE 维度切 chunk 串行 (`moe_chunk_max_len`),代价是 prefill 吞吐下降。源码(`modeling_deepseek.py:L755`): | ||
| 239 | - | ||
| 240 | -```python | ||
| 241 | -if bs_qlen > self.moe_chunk_max_len: # need to chunk moe seq_len dim to avoid OOM | ||
| 242 | - num_chunks = (bs_qlen + self.moe_chunk_max_len - 1) // self.moe_chunk_max_len | ||
| 243 | - # ... chunked serial MoE | ||
| 244 | -``` | ||
| 245 | - | ||
| 246 | -## Benchmark | ||
| 247 | - | ||
| 248 | -基于 Atlas A3,`config/decode_r1_rank_128_128ep_a8w8c8_mtp_benchmark.yaml`,W8A8C8: | ||
| 249 | - | ||
| 250 | -| Quant Mode | Global Batch Size | Seq Length | Chips | TPOT (ms) | Throughput (tokens/p/s) | | ||
| 251 | -| ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 252 | -| W8A8C8 | 6144 | 4096 | 128 | 44.9 | 2138 | | ||
| 253 | - | ||
| 254 | -> 注:性能数据基于 MTP1 与 perfect eplb 配置采集,平均 1 个 draft token 中 accept token 为 0.7 个。 | ||
| 255 | -> | ||
| 256 | -> `README.md` 与 `docs/models/deepseek-r1/deepseek_r1_decode_optimization.md` 对同一 benchmark 分别标注 Chips=64 与 Chips=128,存疑。配合配置文件 `world_size: 128` 判断 128 为正确值,README 表头的 64 可能是笔误,暂以 128 为主。 | ||
| 257 | - | ||
| 258 | -Prefill benchmark(Atlas A3 2 机 16 卡,64×4K 输入,perfect eplb): | ||
| 259 | - | ||
| 260 | -| 切分策略 | 时延 (ms) | 总吞吐 (tokens/s) | 平均吞吐 (tokens/卡/s) | | ||
| 261 | -| ---- | ---- | ---- | ---- | | ||
| 262 | -| MLA-DP32 + MoE-EP32 | 3128 | 83805 | 5237 | | ||
| 263 | -| + micro-batch | 2512 | 104356 | 6522 | | ||
| 264 | - | ||
| 265 | -> 注:docs 使用 "DP32/EP32" 而 README 与 prefill YAML 为 `world_size: 32`,一致,但 prefill 环境描述为 "2 机 16 卡"——数字 16 与 32 不一致,以 DP32/EP32(32 卡)为准。源文档同段存在数字不自洽,待验证。 | ||
| 266 | - | ||
| 267 | ## 架构图(stock HF 基线) | 60 | ## 架构图(stock HF 基线) |
| 268 | 61 | ||
| 269 | DeepSeek-R1 的 **stock HuggingFace 模型级架构**(部署前基线,拓扑按 HF `DeepseekV3ForCausalLM` modeling 整理,与 cann-recipes-infer @2c45117 自带 `configuration_deepseek.py` 默认值逐项一致)。R1 复用 DeepSeek-V3 的架构类(同 `architectures=DeepseekV3ForCausalLM`、`model_type=deepseek_v3`),故此基线即 V3/R1 共用拓扑。该模型级架构与昇腾 Atlas A3 部署优化(MLA-Prolog 融合、Double-Routing、micro-batch、量化、TorchAir 等)互补:一者描述「模型长什么样」,另一者描述「在 NPU 上怎么跑」。溯源见 [`deepseek-ai/DeepSeek-V3/config.json`](https://huggingface.co/deepseek-ai/DeepSeek-V3/blob/e815299b0bcbac849fa540c768ef21845365c9eb/config.json)。 | 62 | DeepSeek-R1 的 **stock HuggingFace 模型级架构**(部署前基线,拓扑按 HF `DeepseekV3ForCausalLM` modeling 整理,与 cann-recipes-infer @2c45117 自带 `configuration_deepseek.py` 默认值逐项一致)。R1 复用 DeepSeek-V3 的架构类(同 `architectures=DeepseekV3ForCausalLM`、`model_type=deepseek_v3`),故此基线即 V3/R1 共用拓扑。该模型级架构与昇腾 Atlas A3 部署优化(MLA-Prolog 融合、Double-Routing、micro-batch、量化、TorchAir 等)互补:一者描述「模型长什么样」,另一者描述「在 NPU 上怎么跑」。溯源见 [`deepseek-ai/DeepSeek-V3/config.json`](https://huggingface.co/deepseek-ai/DeepSeek-V3/blob/e815299b0bcbac849fa540c768ef21845365c9eb/config.json)。 |
| @@ -313,7 +106,13 @@ flowchart TD | |||
| 313 | | MoE | moe_intermediate | 2048 | | 106 | | MoE | moe_intermediate | 2048 | |
| 314 | | MoE | dense_layers | 1–3 only | | 107 | | MoE | dense_layers | 1–3 only | |
| 315 | 108 | ||
| 316 | -其中 `kv_lora_rank=512`、`q_lora_rank=1536` 是 MLA 低秩压缩的两级 latent 维度(KVCache 仅存 `c_KV`=512 + 单份共享 `k_rope`=64),与 [MLA 低秩压缩 + 融合算子](#mla-低秩压缩--融合算子) 一节中 `npu_mla_prolog_v3` 的 Q/K/V 投影路径对应;`n_routed_experts=256` / `n_shared_experts=1` / `top_k=8` 与 [MoE 结构](#moe-结构) 一节一致。MLA 结构细节见 [mla](../modules/mla.md),平台见 [atlas-a3](../platforms/atlas_a3.md)。 | 109 | +其中 `kv_lora_rank=512`、`q_lora_rank=1536` 是 MLA 低秩压缩的两级 latent 维度(KVCache 仅存 `c_KV`=512 + 单份共享 `k_rope`=64),与 [MLA 部署流程](../../guides/models/deepseek_r1_deployment.md) 一节中 `npu_mla_prolog_v3` 的 Q/K/V 投影路径对应;`n_routed_experts=256` / `n_shared_experts=1` / `top_k=8` 与 [MoE 结构](#moe-结构) 一节一致。MLA 结构细节见 [mla](../modules/mla.md),平台见 [atlas-a3](../platforms/atlas_a3.md)。 |
| 110 | + | ||
| 111 | +## 部署与证据入口 | ||
| 112 | + | ||
| 113 | +- [模型部署与验收流程](../../guides/models/deepseek_r1_deployment.md):配置、开关、失效条件与回退入口。 | ||
| 114 | +- [DeepSeek-R1 A3 Prefill 双流的来源性能观测](../../optimizations/benchmark_observations/deepseek_r1_a3_prefill_source_benchmark.md):固定来源自报,未经本仓独立复测。 | ||
| 115 | +- [DeepSeek-R1 A3 Decode 的来源数据与 Chips 分歧](../../optimizations/benchmark_observations/deepseek_r1_a3_decode_source_benchmark.md):固定来源自报,未经本仓独立复测。 | ||
| 317 | 116 | ||
| 318 | ## Evidence | 117 | ## Evidence |
| 319 | 118 | ||
| @@ -344,4 +143,4 @@ flowchart TD | |||
| 344 | - [w8a8c8](../quantization/w8a8c8.md) | 143 | - [w8a8c8](../quantization/w8a8c8.md) |
| 345 | - [deepseek-r1-rl](../../../training/guides/recipes/deepseek_r1_rl.md)(跨域·训练:R1 在 A3 128 卡的 veRL+MindSpeed+vLLM-Ascend 全共卡 GRPO RL 后训练,120+ TPS/卡;本推理部署是其训练目标模型) | 144 | - [deepseek-r1-rl](../../../training/guides/recipes/deepseek_r1_rl.md)(跨域·训练:R1 在 A3 128 卡的 veRL+MindSpeed+vLLM-Ascend 全共卡 GRPO RL 后训练,120+ TPS/卡;本推理部署是其训练目标模型) |
| 346 | - [grpo-dapo](../../../training/concepts/techniques/grpo_dapo.md)(跨域·训练:R1 RL 用 GRPO 算法) | 145 | - [grpo-dapo](../../../training/concepts/techniques/grpo_dapo.md)(跨域·训练:R1 RL 用 GRPO 算法) |
| 347 | -- [eplb](../../../training/concepts/techniques/eplb.md)(跨域·训练:EPLB(DeepSeek 提出的专家并行负载均衡器)) | 146 | +- [eplb](../../../training/concepts/techniques/eplb.md)(跨域·训练:EPLB(DeepSeek 提出的专家并行负载均衡器)) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: DeepSeek-V4 | 3 | title: DeepSeek-V4 |
| 4 | -description: 'DeepSeek-V4 是面向长序列的 DeepSeek 新一代模型,包含 Flash 285B 与 Pro 1.6T。它以 C1A/SWA(窗口 128)、CSA(4:1 压缩后由 LI 选 Top512)和 HCA(128:1 压缩)交错组成 Hybrid Attention,统一使用 MQA/Shared-KV、Attention Sink 和 mHC;128K 序列下 HCA 约保留 1K KV,CSA 约保留 32K。MoE 每 token 激活 6 个路由专家和 1 个共享专家,Flash/Pro 分别有 256/384 个路由专家。Prefill 使用 CP+zigzag,Decode 使用 DP+EP,并支持 MTP1/MTP3。A3 主要采用 W8A8C16 或 INT8-INT4,950PR/DT 支持 FP8/MXFP4;图模式默认 npugraph_ex。性能数据必须同时限定平台、卡数、量化、序列长度和 MTP 配置。' | 4 | +description: 'DeepSeek-V4 是面向长序列的 DeepSeek 新一代模型,包含 Flash 285B 与 Pro 1.6T。它以 C1A/SWA(窗口 128)、CSA(4:1 压缩后由 LI 按权重配置选 Top-k)和 HCA(128:1 压缩)交错组成 Hybrid Attention,统一使用 MQA/Shared-KV、Attention Sink 和 mHC;128K 序列下 HCA 约保留 1K KV,CSA 约保留 32K。MoE 每 token 激活 6 个路由专家和 1 个共享专家,Flash/Pro 分别有 256/384 个路由专家。Prefill 使用 CP+zigzag,Decode 使用 DP+EP,并支持 MTP1/MTP3。A3 主要采用 W8A8C16 或 INT8-INT4,950PR/DT 支持 FP8/MXFP4;图模式默认 npugraph_ex。性能数据必须同时限定平台、卡数、量化、序列长度和 MTP 配置。' |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -12,7 +12,7 @@ sources: | |||
| 12 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_inference_guide.md | 12 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/deepseek-v4/deepseek_v4_inference_guide.md |
| 13 | role: primary | 13 | role: primary |
| 14 | created_at: '2026-05-30T00:00:00Z' | 14 | created_at: '2026-05-30T00:00:00Z' |
| 15 | -updated_at: '2026-09-03T13:55:44Z' | 15 | +updated_at: '2026-10-22T10:35:31Z' |
| 16 | platforms: | 16 | platforms: |
| 17 | - a3 | 17 | - a3 |
| 18 | - '950' | 18 | - '950' |
| @@ -79,39 +79,11 @@ KV Cache 由 **Window Cache + Compress Cache + remainder** 组成(压缩层的 | |||
| 79 | 79 | ||
| 80 | ### mHC(Manifold-Constrained Hyper-Connections) | 80 | ### mHC(Manifold-Constrained Hyper-Connections) |
| 81 | 81 | ||
| 82 | -mHC 是对传统残差连接的扩展:把 `hidden_state` 从一路扩展到多路,在 Attention/MoE 计算前经 Pre Mapping 融合回一路(保持 Attention/MoE 计算过程不变),输出经 Post Mapping 扩展回多路,多路残差经 Res Mapping(Sinkhorn-Knopp 约束的混合矩阵)特征融合后与 Post Mapping 结果加和得到 mHC 输出。结构语义与计算公式见 [mhc](../modules/mhc.md)。本仓提供 `HcPre` / `HcPost` 融合算子(`HcPre` 含 AscendC + PyPTO,`HcPost` 含 AscendC),算子级 Tiling/流水/性能见 [mhc-hcpre-hcpost](../kernels/mhc_hcpre_hcpost.md)。`hc_post` 等小算子组合另有 `@torch.compile` + [autofuse](../frameworks/autofuse.md) 自动融合路径(A3 实测 4x),与手写 AscendC 算子并列。 | 82 | +mHC 是对传统残差连接的扩展:把 `hidden_state` 从一路扩展到多路,在 Attention/MoE 计算前经 Pre Mapping 融合回一路(保持 Attention/MoE 计算过程不变),输出经 Post Mapping 扩展回多路,多路残差经 Res Mapping(Sinkhorn-Knopp 约束的混合矩阵)特征融合后与 Post Mapping 结果加和得到 mHC 输出。结构语义与计算公式见 [mhc](../modules/mhc.md)。本仓提供 `HcPre` / `HcPost` 融合算子(`HcPre` 含 AscendC + PyPTO,`HcPost` 含 AscendC),算子级 Tiling/流水/性能见 [mhc-hcpre-hcpost](../kernels/mhc_hcpre_hcpost.md)。`hc_post` 等小算子组合另有 `@torch.compile` + [autofuse](../frameworks/autofuse.md) 自动融合路径,与手写 AscendC 算子并列。 |
| 83 | 83 | ||
| 84 | ## Serving Strategy | 84 | ## Serving Strategy |
| 85 | 85 | ||
| 86 | -### Prefill 并行(长序列 CP + zigzag) | 86 | +配置、CP 数据交换、MTP 缓存与接受率验收集中到[DeepSeek-V4 部署流程](../../guides/models/deepseek_v4_deployment.md)。本页保留模型结构,具体优化取舍与验证按流程引用的规则执行。 |
| 87 | - | ||
| 88 | -长序列 Prefill 内存与 TTFT 压力大,故 Attention 选 **Context Parallel(CP)** 而非 DP,多 rank 分摊计算/访存。LI 用 `causal_mask`,越靠后的 Context Chunk 访存/计算越重,故沿用 V3.2 的 `zig_zag` 切分将首尾序列块放同一 rank 做负载均衡。见 [context-parallel](../parallelism/context_parallel.md)、[zigzag-cp](../parallelism/zigzag_cp.md)。 | ||
| 89 | - | ||
| 90 | -切分需同时考虑滑窗与压缩:SWA 层每个 chunk 需前序 `win_size=128` 的 KV;HCA / CSA 层若前序 chunk 序列 **128 对齐**则无需向后传额外 token(CSA Compressor 另需前序 4 个 token 做 Overlap)。Attention 模块引入两次 CP 通信:MQA 起始用全局 `all_gather` 取各 rank 末尾 128 token(按 `receive_idx` 裁切,`chunk_0` 不取);Compressor 后对 Compress Cache 做 CP 域 `all_gather`(zigzag 场景需顺序重排)供 LI/SAS 使用。 | ||
| 91 | - | ||
| 92 | -**进一步通信优化**(源 §并行策略 › Prefill 并行策略末段,CSA 为例,源附图 `prefill_cp_allgather_cache2`):由于两个 Compressor 存在并行空间,且 CSA 的 Compressor 仅需 4 个 Overlap token 信息,因此可将 **Window Attention 所需数据**与 **Compressor 所需数据分别进行通信**,实现计算通信掩盖,且冗余计算的数据量也较少。Compressor 后同样需要对 Compressed Cache 做 CP 域 `all_gather` 通信。 | ||
| 93 | - | ||
| 94 | -### Decode 并行(DP + EP + LM_head TP) | ||
| 95 | - | ||
| 96 | -Decode 参考 DeepSeek-R1 / V3.2 方案:Attention 用 **Data Parallel(DP)**、MoE 用 **Expert Parallel(EP)**、LM_head 用 **Tensor Parallel(TP)**。Output Projection 中 `o_a_proj` 权重大、访存重,低时延场景可**选择性对其做 TP 切分**,经 `all_to_all` + `reduce_scatter` 实现 DP↔TP 转换。见 [data-parallel](../parallelism/data_parallel.md)、[expert-parallel](../parallelism/expert_parallel.md)、[tensor-parallel](../parallelism/tensor_parallel.md)。 | ||
| 97 | - | ||
| 98 | -### MTP | ||
| 99 | - | ||
| 100 | -V4 沿用原生 Multi-Token Prediction([mtp-speculative-decoding](../techniques/mtp_speculative_decoding.md))。Main Model 针对 Window KV 与 Compressor 的 `kv_state`/`score_state` 设计 **Ring Buffer**(`block_table` + `slot_mapping` 复用少数 Cache 块),其余 KV Cache 按压缩后最大长度申请;MTP 场景沿用 LI / SAS 内置的**梯形掩码**,确保待验证 token 的压缩 Cache 不参与主模型 token 计算。**MTP Spec Model 仅使用 Window Attention(SWA)**,`next_n` 个 Draft Token 共享一份 MTP 权重和 KV Cache。MTP 在 SWA/HCA/LI 上 Cache 搬运量与非 MTP 几乎一致,自然提升计算访存比;CSA 的 FA 极端下搬运量为非 MTP 的 `next_n+1` 倍。高吞吐用 MTP1、低时延用 MTP3。 | ||
| 101 | - | ||
| 102 | -#### Main Model 额外 Cache 定量(源 §MTP › Main Model 方案) | ||
| 103 | - | ||
| 104 | -若某 MTP Token 校验不被接受,需在下一 Step 重新推导,故须为 Compressor 的 `kv_state`/`score_state` 额外缓存。结合 Ring Buffer,只需**提前申请更大 Buffer** 即可自然实现该额外缓存动作。需缓存的 Cache 以 `compress_ratio` 个 token 为一组统计,组数公式为: | ||
| 105 | - | ||
| 106 | -$$ | ||
| 107 | -\mathrm{num\_ratio\_group} = (1 \ \mathrm{if} \ \mathrm{next\_n} == 0 \ \mathrm{else}\ 2) + \mathrm{overlap} | ||
| 108 | -$$ | ||
| 109 | - | ||
| 110 | -其中 `overlap` 在 CSA 等于 1、在 HCA 等于 0。`num_ratio_group` 里预留了**余数 token** 和**可能需要重新压缩的前序 token**。每个 Batch 需要的 Cache Block 个数为: | ||
| 111 | - | ||
| 112 | -$$ | ||
| 113 | -\mathrm{ceil}(\mathrm{num\_ratio\_group} \times \mathrm{ratio} \ / \ \mathrm{block\_size}) | ||
| 114 | -$$ | ||
| 115 | 87 | ||
| 116 | ## Quantization Overview | 88 | ## Quantization Overview |
| 117 | 89 | ||
| @@ -125,20 +97,7 @@ $$ | |||
| 125 | 97 | ||
| 126 | ## 多流并行优化 | 98 | ## 多流并行优化 |
| 127 | 99 | ||
| 128 | -V4 借助 CANN 软件栈的算子控核与多流并行能力,对跨算子无数据依赖的计算流做细粒度并行掩盖([multi-stream](../techniques/multi_stream.md))。 | 100 | +Attention/Compressor 和共享/路由专家的具体编排与检查集中到[部署流程的多流章节](../../guides/models/deepseek_v4_deployment.md#4-多流数据依赖检查),通用机制见[多流](../techniques/multi_stream.md)。 |
| 129 | - | ||
| 130 | -### Attention 多流并行 | ||
| 131 | - | ||
| 132 | -源 §多流并行优化 › Attention 多流并行(CSA 为例,源附图 `attn_multi_stream`)逐条列出 4 个并行点: | ||
| 133 | - | ||
| 134 | -- **(a)** MLAProlog 模块中 Query 计算流与 KV 计算流有并行空间:`kv_proj`、`q_b_proj` 等 Matmul 用 **Cube 核**计算,与 `q_rms_norm` 等用 **Vector 核**计算的算子可并行; | ||
| 135 | -- **(b)** `q_b_proj` 后的 `rms_norm` 和 `rope` 可与 LI 的 `li_q_b_proj` 并行计算; | ||
| 136 | -- **(c)** CSA 时 LI 与 CSA Compressor 无数据依赖,可通过 **CV 控核**确保算子分配到合适的 Cube/Vector 核数,在多流并行时减少计算资源抢占对性能的影响,从而**完全掩盖 CSA 的 Compressor**; | ||
| 137 | -- **(d)** HCA 中没有 LI,可直接用 MLAProlog 计算掩盖**部分** Compressor。 | ||
| 138 | - | ||
| 139 | -### MoE 共享路由多流并行 | ||
| 140 | - | ||
| 141 | -源 §多流并行优化 › MoE 共享路由多流并行(源附图 `moe_multi_stream`):MoE 模块中共享专家与路由专家存在**计算与计算、计算与通信**的并行机会。本实践针对 **Int8 和 MXFP8 量化场景**实现了专家多流并行,可获得较大收益——以路由专家为关键路径,在**基本不影响路由专家**的同时掩盖共享专家的计算耗时。详见 [moe-multi-stream](../techniques/moe_multi_stream.md)。 | ||
| 142 | 101 | ||
| 143 | ## Platform | 102 | ## Platform |
| 144 | 103 | ||
| @@ -146,28 +105,7 @@ V4 在 Atlas A3 Pod([atlas-a3](../platforms/atlas_a3.md),每节点 8 卡,` | |||
| 146 | 105 | ||
| 147 | ## Benchmark | 106 | ## Benchmark |
| 148 | 107 | ||
| 149 | -> 数据基于 Offline 推理(不含 Serving 调度/负载均衡),MTP 投机 + 强制 EPLB 采集;MTP3 平均 3 个 Draft Token 中 Accept 1.44、MTP1 平均 1 个 Accept 0.7。引用须带全限定(平台 + MTP + DataType + SeqLen)。 | 108 | +来源自报数字分平台统一维护在[950DT 观测](../../optimizations/benchmark_observations/deepseek_v4_950_source_benchmark.md)和[A3 Pod 观测](../../optimizations/benchmark_observations/deepseek_v4_a3_source_benchmark.md)。核对每行 GBS、卡数、MTP、量化和计时缺口;这里不重复性能表。 |
| 150 | - | ||
| 151 | -**950DT,16 卡,DP + EP(节选 DeepSeek-V4 Flash)** | ||
| 152 | - | ||
| 153 | -| 平台 | 规格 | Chips | MTP | DataType | SeqLen | TPOT (ms) | Throughput (Tokens/p/s) | | ||
| 154 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 155 | -| 950DT | Flash | 16 | 3 | Hybrid MXFP8-MXFP4 | 8192 | 9.84 | 1625.31 | | ||
| 156 | -| 950DT | Flash | 16 | 1 | Hybrid MXFP8-MXFP4 | 8192 | 20.33 | 4722.22 | | ||
| 157 | -| 950DT | Flash | 16 | 3 | Hybrid MXFP8-MXFP4 | 131072 | 10.69 | 1496.36 | | ||
| 158 | -| 950DT | Flash | 16 | 3 | Hybrid MXFP8-MXFP4 | 1048576 | 8.22 | 121.70 | | ||
| 159 | -| 950DT | Flash | 16 | 1 | Hybrid FP8-MXFP4 | 8192 | 24.28 | 3953.49 | | ||
| 160 | -| 950DT | Pro | 16 | 3 | Hybrid MXFP8-MXFP4 | 8192 | 17.64 | 56.7 | | ||
| 161 | -| 950DT | Pro | 16 | 3 | Hybrid MXFP8-MXFP4 | 131072 | 19.46 | 51.38 | | ||
| 162 | - | ||
| 163 | -> 注:源 Benchmark 含 Global Batch Size 列(本表略)——Flash 8192/131072 行系 GBS=256(MTP3)/1536(MTP1)大 batch,但 **Flash 1048576(1M)行系 GBS=16** 小 batch(极长序列带宽受限,Throughput 121.70 偏低属正常,不可与同规格大 batch 行横比);950DT **Pro** 行亦系 **GBS=16** 小 batch 采集,故 Pro 的低 Tokens/p/s(56.7/51.38)是小 batch 口径,**不可与 Flash 大 batch 行横比**。 | ||
| 164 | - | ||
| 165 | -**Atlas A3 Pod,64 卡,DP + EP,W8A8C16(DeepSeek-V4 Flash,LI Cache Int8 / KV Cache BF16)** | ||
| 166 | - | ||
| 167 | -| 平台 | 规格 | Chips | Global Batch Size | MTP | DataType | SeqLen | TPOT (ms) | Throughput (Tokens/p/s) | | ||
| 168 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 169 | -| A3 Pod | Flash | 64 | 7168 | 1 | W8A8C16 | 8192 | 27.82 | 4025 | | ||
| 170 | -| A3 Pod | Flash | 64 | 8192 | 1 | W8A8C16 | 8192 | 29.17 | 4388 | | ||
| 171 | 109 | ||
| 172 | ## Future Plan | 110 | ## Future Plan |
| 173 | 111 | ||
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: Gemma-4-26B-A4B | 3 | title: Gemma-4-26B-A4B |
| 4 | -description: 'Gemma-4-26B-A4B 是 26.5B 参数、每 token 激活约 3.8B 的多模态 MoE;当前样例只适配语言 MoE Decoder,不含视觉塔,BF16 权重约 51.6GB,建议 8 卡。30 层按 5:1 交错 25 层 sliding GQA(window=1024)与 5 层 full GQA,full 层 K/V 共享;每层 Dense MLP 与 128-expert、top-8 MoE 并联。两类 Attention 使用不同维度的逐层连续 KV 缓存,不走 Paged Attention。Decode 的 EP8 可选 MC2 或 local_experts,后者保持静态 shape 以适配 GE fullgraph;默认选择还受 A2 每 rank ≤24 experts 限制。当前支持 GE/npugraph_ex,W8A8/W4A16 和视觉塔尚未适配,GEGLU 与 partial RoPE 仍有算子能力缺口。' | 4 | +description: Gemma-4 文本 Decoder 的混合滑窗/全局注意力、并联 Dense/MoE 与异构缓存机制。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,20 +14,20 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/agentic/optimization_report.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/agentic/optimization_report.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-05-31T00:00:00Z' | 16 | created_at: '2026-05-31T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 17 | +updated_at: '2026-10-22T10:36:46Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a3 | 19 | - a3 |
| 20 | --- | 20 | --- |
| 21 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 22 | # Gemma-4-26B-A4B | 24 | # Gemma-4-26B-A4B |
| 23 | 25 | ||
| 24 | ## Summary | 26 | ## Summary |
| 25 | 27 | ||
| 26 | -Gemma-4-26B-A4B 是 Google 2026 年开源的多模态 MoE 大语言模型,包含视觉塔(Vision Tower)与语言 MoE 解码器两部分。cann-recipes-infer 的样例**仅适配 Language MoE Decoder 路径,视觉塔不在覆盖范围内**;建模代码基于 transformers 的 `modeling_gemma4` 改写为昇腾自研 executor([cann-executor](../frameworks/cann_executor.md))适配(源内版本号不一致:README/`requirements.txt` pin `transformers==5.0.0`,而 `modeling_gemma4.py` 文件头标注 adapted from v5.5.0)。 | 28 | +Gemma-4-26B-A4B的本卡范围是文本Language MoE Decoder;视觉塔属于另一部分,不据文本部署推导已适配。30层包含5:1的sliding/full GQA,每层Dense MLP与MoE并联,词表Embedding与LMHead共享。 |
| 27 | 29 | ||
| 28 | -规模:总参数量 **26.5B**,每 token 激活 **~3.8B**(128 experts top-8),权重 **~51.6 GB(BF16)**,建议 8 卡部署。目标平台 Atlas A2 / A3(源 README/报告双平台 benchmark;wiki 仅 [atlas-a3](../platforms/atlas_a3.md) 有页,A2 无独立平台页)。 | 30 | +Sliding/full的head维度和K=V关系影响缓存形状;具体平台开关、图后端与质量验收由Guide维护。平台配置、开关和验收步骤见[模型部署流程](../../guides/models/gemma4_26b_a4b_deployment.md),此处只保留模型机制。 |
| 29 | - | ||
| 30 | -最独特的三处结构/工程点:**双模式 Attention**、**异构连续缓存**、**MoE EP decode 双路径(mc2 / local_experts)**。 | ||
| 31 | 31 | ||
| 32 | ## 架构(仅 Language Decoder) | 32 | ## 架构(仅 Language Decoder) |
| 33 | 33 | ||
| @@ -52,7 +52,7 @@ Token Embedding(vocab=262144,与 LM Head 共享权重)→ Decoder Block × | |||
| 52 | 52 | ||
| 53 | ## 异构连续缓存(KVCache) | 53 | ## 异构连续缓存(KVCache) |
| 54 | 54 | ||
| 55 | -双模式 Attention 导致 KV 维度异构:sliding 层 `kv_dim = N_kv×head_dim = 8×256 = 2048`;full 层 `2×512 = 1024`(k_eq_v,K/V 共享)。Gemma-4 采用**连续缓存(非 Paged)**: | 55 | +双模式 Attention 导致 KV 维度异构:sliding 层 `kv_dim = N_kv×head_dim = 8×256 = 2048`;full 层 `2×512 = 1024`(单份 K 或 V 的宽度;k_eq_v 共享投影权重,不共享缓存)。投影结果 clone 后,K/V 分别归一化,且仅 K 施加 RoPE,随后分别写入 k_cache/v_cache;内存预算不能把两份缓存合并。Gemma-4 采用**连续缓存(非 Paged)**: |
| 56 | 56 | ||
| 57 | - 每层独立分配缓冲(`cache_unit = N_kv×head_dim`),sliding/full shape 不同,避免统一 Paged-Attention block 格式维护两套 block_table 的复杂度。 | 57 | - 每层独立分配缓冲(`cache_unit = N_kv×head_dim`),sliding/full shape 不同,避免统一 Paged-Attention block 格式维护两套 block_table 的复杂度。 |
| 58 | - `attn_tp=1` 无 KV head 切分需求,连续缓存足够。 | 58 | - `attn_tp=1` 无 KV head 切分需求,连续缓存足够。 |
| @@ -61,47 +61,10 @@ Token Embedding(vocab=262144,与 LM Head 共享权重)→ Decoder Block × | |||
| 61 | 61 | ||
| 62 | ## FA 与 RoPE | 62 | ## FA 与 RoPE |
| 63 | 63 | ||
| 64 | -- FA 走 **FIA v1**([fused-infer-attention-score](../kernels/fused_infer_attention_score.md) `npu_fused_infer_attention_score`,输入布局 BSH);图模式 Decode 用 `tng.ops` 同名算子、Prefill 用 `torch.ops.npu` 同名算子。 | 64 | +- FA 走 **FIA v1**([fused-infer-attention-score](../kernels/fused_infer_attention_score.md) `npu_fused_infer_attention_score`,输入布局 BSH);仅 `ge_graph` Decode 用 `tng.ops` 同名算子;`npugraph_ex` 和 Prefill 使用 `torch.ops.npu` 同名算子。 |
| 65 | - Sliding 层 Prefill/Decode:`sparse_mode=4` + `pre_tokens=sliding_window(1024)`(即 sliding window);Full 层按阶段不同:Prefill `sparse_mode=3`(标准 causal,`pre_tokens=INT32_MAX`)、Decode `sparse_mode=0`(`pre_tokens=INT32_MAX`,`modeling_gemma4.py:789-822`)。 | 65 | - Sliding 层 Prefill/Decode:`sparse_mode=4` + `pre_tokens=sliding_window(1024)`(即 sliding window);Full 层按阶段不同:Prefill `sparse_mode=3`(标准 causal,`pre_tokens=INT32_MAX`)、Decode `sparse_mode=0`(`pre_tokens=INT32_MAX`,`modeling_gemma4.py:789-822`)。 |
| 66 | - Sliding 全维 RoPE 用 `npu_rotary_mul`(`rotary_mode='half'`);Full 层 **partial RoPE**(`partial_rotary_factor=0.25`,rotary_dim=128)用 slice + `npu_rotary_mul` + cat 包装实现(`npu_rotary_mul` 无原生 partial 支持,见算子 gap)。 | 66 | - Sliding 全维 RoPE 用 `npu_rotary_mul`(`rotary_mode='half'`);Full 层 **partial RoPE**(`partial_rotary_factor=0.25`,rotary_dim=128)用 slice + `npu_rotary_mul` + cat 包装实现(`npu_rotary_mul` 无原生 partial 支持,见算子 gap)。 |
| 67 | 67 | ||
| 68 | -## 图模式 + MoE EP 双路径 | ||
| 69 | - | ||
| 70 | -图模式默认 GE graph(torchair `fullgraph=True`,Decode 整图、Prefill 保持 eager),可切 `exe_mode: npugraph_ex`(torch.compile aclgraph,代码共用)。详见 [npu-graph-mode](../techniques/npu_graph_mode.md)(ge_graph / npugraph_ex 两后端对比)。ge_graph Decode 整图能成立依赖两处去 host 同步的图化改造:cos/sin 在 `init_pa_cache` 时预计算为 buffer、graph 内直接索引(避免 `kv_len.max().item()` 引入 host 同步);`actual_seq_lengths_kv` 取 Tensor 形态(与 `tng.ops` 接口匹配,避免 list 元素变化触发重编译)——故 ge_graph 用 `torchair.ops`(接受 Tensor)、npugraph_ex 走 `torch.ops.npu`(dynamo 强制 `SymInt[]` 必须为 host list),二者在此分叉(`modeling_gemma4.py:725-739`)。 | ||
| 71 | - | ||
| 72 | -**MoE EP decode 双路径**(`Gemma4SparseMoeBlock`,开关 `model_config.custom_params.moe_ep_decode_mode`): | ||
| 73 | - | ||
| 74 | -- **`mc2`**:`npu_moe_distribute_dispatch_v2` + `npu_moe_distribute_combine_v2`([moe-dispatch-combine-v2](../techniques/moe_dispatch_combine_v2.md)),与仓内其他 MoE 模型一致。 | ||
| 75 | -- **`local_experts`**:每 rank 计算全部 16 个本地 expert(非本 rank expert 的 routing weight 置 0),经 `npu_moe_init_routing` + `npu_moe_finalize_routing`,末次 `all_reduce`(`moe_ep_group`)汇总。完全静态 shape,对 ge_graph fullgraph 友好;Gemma-4 无零专家。 | ||
| 76 | - | ||
| 77 | -代码按 `experts_per_rank ≤ 24` 自动选默认(`default_mode = "mc2" if experts_per_rank<=24 else "local_experts"`,因 `npu_moe_distribute_dispatch_v2` 在 A2 受 ≤24 限制、A3 无);EP=8 下 16 experts/rank 两平台都满足,故默认 `mc2`;yaml 可显式覆盖(仓内 `gemma4_rank_8_8ep_gegraph_decode.yaml` 实际写 `local_experts`,doc≠config:报告称默认 mc2,shipped yaml 覆盖为 local_experts)。 | ||
| 78 | - | ||
| 79 | -> A3 实测(8 卡 EP=8,BS=8,input_len=256)两路径输出一致、性能接近(ge_graph decode:local_experts 10.20 ms vs mc2 10.50 ms);小 batch 下 mc2 的 2 次 AllToAll + padding 固定开销摊不开,mc2 相对优势在更大 batch / Prefill 才显现。 | ||
| 80 | - | ||
| 81 | -## 并行切分 | ||
| 82 | - | ||
| 83 | -| 模块 | 切分 | 依据 | | ||
| 84 | -|---|---|---| | ||
| 85 | -| Attention | `attn_tp=1`([data-parallel](../parallelism/data_parallel.md) DP=8)| full 层 N_kv=2 致 TP 上限 2;DP=8 最大化吞吐 | | ||
| 86 | -| Dense MLP | `dense_tp=1` | intermediate=2112 较小,TP 切碎后 cube 利用率低 | | ||
| 87 | -| MoE | `EP=8`([expert-parallel](../parallelism/expert_parallel.md))| 128 experts / 8 = 16 experts/rank | | ||
| 88 | -| Embedding / LM Head | `embed_tp=8` / `lmhead_tp=8` | 大词表 262144 沿 vocab 切分 | | ||
| 89 | - | ||
| 90 | -## 性能(带平台 + 配置前提,A2 与 A3 不混比) | ||
| 91 | - | ||
| 92 | -测试条件:BS=8、input_len=256、max_new_tokens=32、8 卡、BF16。 | ||
| 93 | - | ||
| 94 | -- **A2 累计演进**(报告 §6.1/§8 累计表):eager 基线 decode 98.5 ms → 图模式 decode 18.21 ms(**累计 5.42×**,主要来自 ge_graph Decode 整图 + local_experts MoE);prefill 312.5 → 302.5 ms。 | ||
| 95 | -- **A3 vs A2(同 ge_graph 路径,报告 §7 跨硬件表 / README)**:prefill 76.43 ms(**2.47×**)/ decode 10.20 ms(**1.45×**)。 | ||
| 96 | -- ⚠️ 源内 A2 ge_graph 有两套测值:累计演进表(§6.1/§8)prefill 302.5 / decode 18.21 ms,跨硬件表(§7/README)prefill 189 / decode 15 ms(测试口径不同)。上面 2.47×/1.45× 跨硬件加速比按 §7 表算(A2 基线 189/15),不与累计表 302.5/18.21 混算。 | ||
| 97 | - | ||
| 98 | -## 算子 / 量化 gap(§9 / §10) | ||
| 99 | - | ||
| 100 | -- `npu_rotary_mul` 无 `partial_rotary_factor` 原生支持 → full 层 partial RoPE 需 slice + 融合 + cat 包装。 | ||
| 101 | -- `npu_geglu` 未注册图模式 dispatch(torchair 无 converter;torch.compile meta 签名问题)→ Decode 图模式走手动 `torch_npu.npu_fast_gelu(gate) * up`(等价 `gelu(approximate='tanh')`;刻意不用 `F.gelu`——其在 aclgraph capture 下走 CPU fallback,`modeling_gemma4.py:315-318`),Prefill/eager 仍用融合 `npu_geglu` 高精度。30 层 MoE 专家手写 GEGLU 致 Decode 与 eager **非 bit-exact**(语义一致,差异为 BF16 舍入累积;Prefill 首 token logit 与 eager 完全一致)。bit-exact 对齐待算子侧补齐。 | ||
| 102 | -- `npu_ffn`(geglu 模式)仅 FP16、`npu_gelu_mul` dim≤1024,故 BF16 模型无法走融合 GEGLU 路径。 | ||
| 103 | -- **W8A8 / W4A16 量化路径未接入**;Vision Tower 未适配;sliding 层环形缓存压缩未做。 | ||
| 104 | - | ||
| 105 | ## 架构图(stock HF 基线) | 68 | ## 架构图(stock HF 基线) |
| 106 | 69 | ||
| 107 | 下图为 Gemma-4-26B-A4B-it 的 **stock HF 架构基线**(部署前先验),拓扑由 HF `config.json` + `transformers` `modeling_gemma4.py` 合成,与上述昇腾 NPU 部署优化([npu-graph-mode](../techniques/npu_graph_mode.md) 图模式 / [expert-parallel](../parallelism/expert_parallel.md) MoE EP 双路径 / 异构连续缓存等)互补——前者是结构先验,后者是部署落地。溯源:`google/gemma-4-26B-A4B-it/config.json` + `huggingface/transformers` `src/transformers/models/gemma4/modeling_gemma4.py`。 | 70 | 下图为 Gemma-4-26B-A4B-it 的 **stock HF 架构基线**(部署前先验),拓扑由 HF `config.json` + `transformers` `modeling_gemma4.py` 合成,与上述昇腾 NPU 部署优化([npu-graph-mode](../techniques/npu_graph_mode.md) 图模式 / [expert-parallel](../parallelism/expert_parallel.md) MoE EP 双路径 / 异构连续缓存等)互补——前者是结构先验,后者是部署落地。溯源:`google/gemma-4-26B-A4B-it/config.json` + `huggingface/transformers` `src/transformers/models/gemma4/modeling_gemma4.py`。 |
| @@ -157,6 +120,12 @@ flowchart TD | |||
| 157 | 120 | ||
| 158 | 注:图的 HF `config.json` 给出 full 层 `num_global_key_value_heads=2`,与正文报告/部署值(N_kv=2)一致——印证了上文「待验证」的取值(源默认回退 8;此处 HF config 确认为 2)。 | 121 | 注:图的 HF `config.json` 给出 full 层 `num_global_key_value_heads=2`,与正文报告/部署值(N_kv=2)一致——印证了上文「待验证」的取值(源默认回退 8;此处 HF config 确认为 2)。 |
| 159 | 122 | ||
| 123 | +## 部署与证据入口 | ||
| 124 | + | ||
| 125 | +- [模型部署与验收流程](../../guides/models/gemma4_26b_a4b_deployment.md):配置、开关、失效条件与回退入口。 | ||
| 126 | +- [Gemma-4 A2 图模式的两组来源性能口径](../../optimizations/benchmark_observations/gemma4_a2_source_benchmark.md):固定来源自报,未经本仓独立复测。 | ||
| 127 | +- [Gemma-4 A3 图后端与 EP 路由的来源观测](../../optimizations/benchmark_observations/gemma4_a3_source_benchmark.md):固定来源自报,未经本仓独立复测。 | ||
| 128 | + | ||
| 160 | ## Evidence | 129 | ## Evidence |
| 161 | 130 | ||
| 162 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gemma4_26b_a4b/agentic/optimization_report.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/agentic/optimization_report.md)(§1.1 架构、§3 并行、§4 KVCache/FA/RoPE、§5 融合、§6 图模式 + §6.3 mc2 vs local_experts、§7 A2↔A3、§8 累计、§9 算子需求、§10 未覆盖项)+ [`README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/README.md)(概述 / 规模 / Agent 工作流 / 产品型号 / CANN 8.5.0)。 | 131 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gemma4_26b_a4b/agentic/optimization_report.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/agentic/optimization_report.md)(§1.1 架构、§3 并行、§4 KVCache/FA/RoPE、§5 融合、§6 图模式 + §6.3 mc2 vs local_experts、§7 A2↔A3、§8 累计、§9 算子需求、§10 未覆盖项)+ [`README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/gemma4_26b_a4b/README.md)(概述 / 规模 / Agent 工作流 / 产品型号 / CANN 8.5.0)。 |
| @@ -173,4 +142,4 @@ flowchart TD | |||
| 173 | - [fused-infer-attention-score](../kernels/fused_infer_attention_score.md) | 142 | - [fused-infer-attention-score](../kernels/fused_infer_attention_score.md) |
| 174 | - [kv-cache-manager](../frameworks/kv_cache_manager.md) | 143 | - [kv-cache-manager](../frameworks/kv_cache_manager.md) |
| 175 | - [cann-executor](../frameworks/cann_executor.md) | 144 | - [cann-executor](../frameworks/cann_executor.md) |
| 176 | -- [inference-config](../techniques/inference_config.md) | 145 | +- [inference-config](../techniques/inference_config.md) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: GLM-5 | 3 | title: GLM-5 |
| 4 | -description: GLM-5 是智谱(Z.ai)744B-A40B MoE 大模型,网络结构与 DeepSeek-V3.2-Exp 完全一致(DSA:MLA 主干 + Lightning Indexer 稀疏选择),cann-recipes-infer 在 Atlas A3 上的并行策略与性能优化方案直接沿用 V3.2-Exp。覆盖 GlmMoeDsaForCausalLM 规格(78 层、6144 hidden、256 路由+1 共享专家 top-8)、DSA 注意力(MLAProlog + MLA 吸收 + GlmMoeDsaIndexer index_topk 2048 + CP 路径)、MoE EP 三路实现(double-routing / dispatch_v2+combine_v2 + 独立通信域 fullmesh_v2 / multi-stream)、sigmoid+noaux_tc 路由、W8A8C16 量化、CP 长序列(65536)+Attention DP、Embed/LMHead TP16、MoE EP 64/128、MTP3、KVCache Offload。Atlas A3 W8A8 decode 64 chips TPOT 22.54ms / 177.46 tokens/p/s。 | 4 | +description: GLM-5 的 DSA、MLA/Indexer 与 MoE 规格;平台配置、投机解码与性能记录分别维护。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -16,21 +16,21 @@ sources: | |||
| 16 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/glm-5/config/glm_5_rank_64_64ep_w8a8_prefill_benchmark.yaml | 16 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/glm-5/config/glm_5_rank_64_64ep_w8a8_prefill_benchmark.yaml |
| 17 | role: supporting | 17 | role: supporting |
| 18 | created_at: '2026-06-03T00:00:00Z' | 18 | created_at: '2026-06-03T00:00:00Z' |
| 19 | -updated_at: '2026-08-25T06:41:27Z' | 19 | +updated_at: '2026-10-22T10:36:46Z' |
| 20 | platforms: | 20 | platforms: |
| 21 | - a3 | 21 | - a3 |
| 22 | --- | 22 | --- |
| 23 | 23 | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 24 | # GLM-5 | 27 | # GLM-5 |
| 25 | 28 | ||
| 26 | ## Summary | 29 | ## Summary |
| 27 | 30 | ||
| 28 | -GLM-5 是智谱(Z.ai)发布的 MoE 大模型,总参 **744B / 激活 40B**(官方称 GLM-5 系列从 355B-A32B 扩展到 744B-A40B;HuggingFace 仅发布 `zai-org/GLM-5`(744B/40B)与 `zai-org/GLM-5-FP8` 两份权重)。其网络结构与 [deepseek-v3.2-exp](deepseek_v3_2_exp.md) **完全一致**——同款 DeepSeek Sparse Attention (DSA):MLA 主干 + Lightning Indexer 稀疏选择;因此 cann-recipes-infer 在 GLM-5 上的**并行策略与性能优化方案均直接沿用 V3.2-Exp**(README 明述)。 | 31 | +GLM-5 在本卡对应744B/激活40B的MoE检查点,采用MLA主干与Lightning Indexer组成的DSA。与DeepSeek-V3.2-Exp为机制同族,层数、维度、词表和实现参数不同,不能把“沿用方案”解释成所有配置及代码完全相同。 |
| 29 | 32 | ||
| 30 | -cann-recipes-infer 在 **Atlas A3** 集群上的 GLM-5 部署涉及:模型规格(`glm_moe_dsa`)、DSA 注意力算子谱系、MoE EP 三路实现、W8A8C16 量化、CP 长序列 + Attention DP 并行、MTP3 与 KVCache Offload。算子机理与 DSA 计算流的完整展开见 [deepseek-v3.2-exp](deepseek_v3_2_exp.md) 与各 module / kernel 页。 | 33 | +平台配置、开关和验收步骤见[模型部署流程](../../guides/models/glm_5_deployment.md),此处只保留模型机制。 |
| 31 | - | ||
| 32 | -- **平台限定**:仅 Atlas A3 系列(镜像 `cann8.5_pt2.8.0_glm_aarch_image_v0.1`,驱动 Ascend HDK 25.2.0)。两 die 一卡,`world_size = 2 × chip_num`。见 [atlas-a3](../platforms/atlas_a3.md)。 | ||
| 33 | -- **结构同源**:`GlmMoeDsaForCausalLM`(`model_type=glm_moe_dsa`)的 DSA / MoE / 量化算子谱系与 V3.2-Exp 同构,差异仅在超参(vocab、层数、head 维、MoE 专家数等)。 | ||
| 34 | 34 | ||
| 35 | ## 架构图(stock HF 基线) | 35 | ## 架构图(stock HF 基线) |
| 36 | 36 | ||
| @@ -61,17 +61,16 @@ flowchart TD | |||
| 61 | 61 | ||
| 62 | 精简参数表(与部署用 `configuration_glm.py` 默认值一致): | 62 | 精简参数表(与部署用 `configuration_glm.py` 默认值一致): |
| 63 | 63 | ||
| 64 | -| 维度 | 值 | | MoE | 值 | | 64 | +| 参数 | 值 | |
| 65 | -| ---- | ---- | ---- | ---- | ---- | | 65 | +| --- | --- | |
| 66 | -| vocab | 154880 | | n_routed_experts | 256 | | 66 | +| vocab / hidden / n_layers | 154880 / 6144 / 78 | |
| 67 | -| hidden | 6144 | | n_shared_experts | 1 | | 67 | +| n_routed_experts / n_shared_experts / top_k | 256 / 1 / 8 | |
| 68 | -| n_layers | 78 | | num_experts_per_tok | 8 | | 68 | +| dense intermediate / moe intermediate | 12288 / 2048 | |
| 69 | -| dense intermediate | 12288 | | routed_scaling_factor | 2.5 | | 69 | +| dense层 / MoE层(从1计数) | 1–3 / 4–78 | |
| 70 | -| moe_intermediate | 2048 | | n_group / topk_group | 1 / 1 | | 70 | +| routed_scaling_factor / n_group / topk_group | 2.5 / 1 / 1 | |
| 71 | -| dense 层 | 1-3(共 3)| | scoring | sigmoid | | 71 | +| scoring / balance | sigmoid / noaux_tc | |
| 72 | -| MoE 层 | 4-78(共 75)| | balance | noaux_tc(无辅助损失)| | 72 | +| max_position_embeddings / rms_norm_eps | 202752 / 1e-5 | |
| 73 | -| max_position_embeddings | 202752 | | norm_topk_prob | True | | 73 | +| norm_topk_prob / tie_word_embeddings | True / False | |
| 74 | -| rms_norm_eps | 1e-5 | | tie_word_embeddings | False | | ||
| 75 | 74 | ||
| 76 | > 层类型由 `GlmMoeDsaConfig` 硬编码 `mlp_layer_types = ["dense"]×3 + ["sparse"]×75`,不通过 `first_k_dense_replace` 配置。 | 75 | > 层类型由 `GlmMoeDsaConfig` 硬编码 `mlp_layer_types = ["dense"]×3 + ["sparse"]×75`,不通过 `first_k_dense_replace` 配置。 |
| 77 | 76 | ||
| @@ -81,64 +80,24 @@ GLM-5 的注意力是 `GlmMoeDsaAttention`(`modeling_glm.py:661`),即 V3.2 | |||
| 81 | 80 | ||
| 82 | - **MLA 维度**:MHA(`num_attention_heads = num_key_value_heads = 64`)。`q_lora_rank 2048`、`kv_lora_rank 512`;`qk_nope_head_dim 192 + qk_rope_head_dim 64 = qk_head_dim 256`、`v_head_dim 256`、`head_dim 64`(= rope 维)。 | 81 | - **MLA 维度**:MHA(`num_attention_heads = num_key_value_heads = 64`)。`q_lora_rank 2048`、`kv_lora_rank 512`;`qk_nope_head_dim 192 + qk_rope_head_dim 64 = qk_head_dim 256`、`v_head_dim 256`、`head_dim 64`(= rope 维)。 |
| 83 | - **Indexer(DSA)**:`GlmMoeDsaIndexer`(`indexer.py:51`),`index_n_heads 32`、`index_head_dim 128`、`index_topk 2048`、`indexer_rope_interleave True`。每 q token 由 Indexer 选出 TopK=2048 个相关 kv 后再算注意力。 | 82 | - **Indexer(DSA)**:`GlmMoeDsaIndexer`(`indexer.py:51`),`index_n_heads 32`、`index_head_dim 128`、`index_topk 2048`、`indexer_rope_interleave True`。每 q token 由 Indexer 选出 TopK=2048 个相关 kv 后再算注意力。 |
| 84 | -- **算子路径**:MLAProlog 走 `npu_mla_prolog_v3`(`mlaprolog_prefill:1032` / `mlaprolog_decode:1172`)→ MLA **吸收**计算 `forward_absorb`(`:923`)→ CP 路径 `forward_absorb_cp`(`:969`,仅 Prefill 长序列)。Prefill 与 Decode 统一走 Absorb(与 V3.2-Exp 方案三一致)。算子层面见 [mla-prolog](../kernels/mla_prolog.md)、[sparse-flash-attention](../kernels/sparse_flash_attention.md)、[lightning-indexer](../kernels/lightning_indexer.md)。 | 83 | +- **算子路径**:先按 `cp_size > 1 and is_prefill` 选择 `forward_absorb_cp`,否则选择 `forward_absorb`;两者是替代分支,各自在内部准备 Q/KV,并执行 MLAProlog、稀疏注意力和 Epilog,不串行调用。算子层面见 [mla-prolog](../kernels/mla_prolog.md)、[sparse-flash-attention](../kernels/sparse_flash_attention.md)、[lightning-indexer](../kernels/lightning_indexer.md)。 |
| 85 | 84 | ||
| 86 | ## MoE(256 路由 + 1 共享,TopK=8) | 85 | ## MoE(256 路由 + 1 共享,TopK=8) |
| 87 | 86 | ||
| 88 | `GlmMoeDsaMoE`(`modeling_glm.py:186`):256 routed + 1 shared expert,每 token 选 8 个路由专家(`top_k + shared = 9` 激活),`routed_scaling_factor 2.5`。 | 87 | `GlmMoeDsaMoE`(`modeling_glm.py:186`):256 routed + 1 shared expert,每 token 选 8 个路由专家(`top_k + shared = 9` 激活),`routed_scaling_factor 2.5`。 |
| 89 | 88 | ||
| 90 | - **路由**:`scoring_func=sigmoid`(`logits.sigmoid()`,`:291`)+ `topk_method=noaux_tc`(无辅助损失均衡,`:258`)。`n_routed_experts ∈ [256, 384]` 时走融合算子 `npu_moe_gating_top_k`(`norm_type=1` 即 sigmoid,`:276`)。 | 89 | - **路由**:`scoring_func=sigmoid`(`logits.sigmoid()`,`:291`)+ `topk_method=noaux_tc`(无辅助损失均衡,`:258`)。`n_routed_experts ∈ [256, 384]` 时走融合算子 `npu_moe_gating_top_k`(`norm_type=1` 即 sigmoid,`:276`)。 |
| 91 | -- **EP 三路实现**(`forward:397`):`moe_tp_size>1` → `moe_infer_tp`;否则按阶段分流——Prefill 走 `moe_infer_double_routing`(`:574`,用 `npu_moe_init_routing_v2` / `npu_moe_re_routing` / `npu_moe_finalize_routing`),Decode 走 `moe_infer_dispatch_combine`(`:610`,一体化融合算子)。 | ||
| 92 | -- **MC2 / dispatch-combine-v2**:`set_mc2_kwargs`(`:364`)取**独立通信域** `moe_ep_group_mc2`(`:1941`)+ `comm_alg "fullmesh_v2"`(`:381`);Decode 调 `npu_moe_distribute_dispatch_v2`(`:624`)/ `npu_moe_distribute_combine_v2`(`:655`),`a8` 量化时 `quant_mode=2`。属 893d72e「独立通信域 + fullmesh_v2」清单内。见 [moe-dispatch-combine-v2](../techniques/moe_dispatch_combine_v2.md)、[double-routing](../techniques/double_routing.md)、[expert-parallel](../parallelism/expert_parallel.md)。 | ||
| 93 | -- **多流**:shared expert 与路由专家 Gating/Dispatch 无依赖,经 `npu_stream_switch` + tagged event 并发掩盖(`:407-418`),由 `enable_multi_streams: True` 使能。思想见 V3.2-Exp 页多流章节。 | ||
| 94 | - | ||
| 95 | -## 量化(W8A8C16) | ||
| 96 | - | ||
| 97 | -- FP8 原始权重经 `utils/weight_convert.sh --quant_mode w8a8c16` 转为 **Int8** 权重,KVCache 维持 **C16**(BF16)。脚本同时支持 `bfloat16 / w8a8c16 / w8a8c8 / w4a8c8` 四种模式(后两者复用 V3.2-Exp 的量化参数包),但 GLM-5 的 benchmark 配置(`config/*.yaml` + README)走 **W8A8(w8a8c16)** 路径。 | ||
| 98 | -- **MoE 量化**:`gmm_quant_mode` 支持 `w8a8int8` / `w4a8int4`;W4A8 时 expert GMM `bias=True`(`:224`)。 | ||
| 99 | -- **激活/SwiGLU 算子**:`npu_dequant_swiglu_quant`(量化路径,`GlmMoeDsaMLP.forward_w8a8int8:120`)/ `npu_swiglu`(非量化,`:117`)。 | ||
| 100 | -- 量化机理(逐模块策略、精度评测)与 V3.2-Exp 同构,见 [w8a8c8](../quantization/w8a8c8.md) 及 V3.2-Exp 页量化章节。 | ||
| 101 | - | ||
| 102 | -## 并行与部署(Atlas A3) | ||
| 103 | - | ||
| 104 | -部署 YAML 均 `exe_mode: ge_graph`([npu-graph-mode](../techniques/npu_graph_mode.md)),`world_size = 2 × chip_num`。Int8 Prefill / Decode 均支持 **8-128 卡**(README)。 | ||
| 105 | - | ||
| 106 | -| 阶段 | YAML | world_size | cp_size | attn_tp | oproj_tp | dense_tp | moe_tp | embed/lmhead_tp | batch | input_max_len | | ||
| 107 | -| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 108 | -| Prefill | `glm_5_rank_64_64ep_w8a8_prefill_benchmark.yaml` | 64 | 64 | 1 | 1 | 1 | 1 | 16 | 64 | 65536 | | ||
| 109 | -| Decode | `glm_5_rank_128_128ep_w8a8_decode_benchmark.yaml` | 128 | 128 | 1 | 4 | 4 | 1 | 16 | 256 | 65536 | | ||
| 110 | - | ||
| 111 | -- **CP(长序列)**:`cp_size` 仅 Prefill 生效且 `cp_size = world_size`(长序列 65536,数据集 InfiniteBench `longbook_qa_eng`);Decode 不启 CP。多 rank 均摊长序列 Attention 计算降低 TTFT,机理见 [context-parallel](../parallelism/context_parallel.md)。 | ||
| 112 | -- **Attention DP**:`attn_tp_size=1` 即 attention 数据并行;`oproj_tp_size` 独立切(仅 `attn_tp_size==1` 时支持)——Decode 取 `oproj_tp 4 / dense_tp 4`,Prefill 取 1。 | ||
| 113 | -- **Embed / LMHead TP**:`embed_tp_size = lmhead_tp_size = 16`(A3 单 node 8 卡 × 2 die = 16,TP 域控制在 HCCS 内)。 | ||
| 114 | -- **MoE EP**:64ep(Prefill)/ 128ep(Decode),`moe_tp_size=1`。 | ||
| 115 | -- **其它**:`enable_weight_nz: True`、`pa_block_size: 128`(见 [weight-nz](../techniques/weight_nz.md))、`enable_multi_streams: True`、`enable_online_split_weight: True`、`perfect_eplb` 可选。 | ||
| 116 | - | ||
| 117 | -## MTP(next_n=3) | ||
| 118 | - | ||
| 119 | -`GlmMoeDsaModelMTPLayer`(`modeling_glm.py:1693`)+ `GlmMoeDsaForCausalLM(is_mtp)`(`:1748`)。`next_n` 支持 0-3,两份 benchmark yaml 均设 `next_n: 3`(MTP3)。Benchmark 注:MTP3 + perfect eplb,平均 3 个 draft token 中 accept **1.44** 个。机理见 [mtp-speculative-decoding](../techniques/mtp_speculative_decoding.md)。 | ||
| 120 | - | ||
| 121 | -> stock HF 的 `glm_moe_dsa` 建模路径不含 MTP 头(config 的 `num_nextn_predict_layers` 未被读取);cann-recipes 的 MTP 是部署侧的投机解码实现,与 V3.2-Exp 同款。 | ||
| 122 | - | ||
| 123 | -## KVCache Offload(长序列) | ||
| 124 | - | ||
| 125 | -长序列 KVCache 经 `offload_cache.OffloadCache`(`modeling_glm.py:54` 引入,attention forward 透传 `offload_cache`)卸载到 Host 内存以提升 batch。机理与 V3.2-Exp 一致(Decode 用相邻 token TopK 命中率复用设备端 cache),见 [kvcache-offload](../techniques/kvcache_offload.md)。 | ||
| 126 | - | ||
| 127 | -## Benchmark | ||
| 128 | - | ||
| 129 | -基于 Atlas A3,`config/glm_5_rank_128_128ep_w8a8_decode_benchmark.yaml`,MTP3 + perfect eplb: | ||
| 130 | - | ||
| 131 | -| Quant Mode | Global Batch | Seq Length | Chips | TPOT (ms) | Throughput (tokens/p/s) | | ||
| 132 | -| ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 133 | -| W8A8 | 256 | 65536 | 64 | 22.54 | 177.46 | | ||
| 134 | - | ||
| 135 | -> 平均 3 个 draft token 中 accept 1.44 个。 | ||
| 136 | 90 | ||
| 137 | ## Notes | 91 | ## Notes |
| 138 | 92 | ||
| 139 | - **与 V3.2-Exp 的关系**:GLM-5 是 cann-recipes 基于 GLM-5 开源代码迁移到 CANN 的样例,结构与方案均沿用 [deepseek-v3.2-exp](deepseek_v3_2_exp.md);DSA / MoE / 量化 / 并行的完整机理与图示见 [deepseek-v3.2-exp](deepseek_v3_2_exp.md),此处记 GLM-5 的规格差异与部署配置。 | 93 | - **与 V3.2-Exp 的关系**:GLM-5 是 cann-recipes 基于 GLM-5 开源代码迁移到 CANN 的样例,结构与方案均沿用 [deepseek-v3.2-exp](deepseek_v3_2_exp.md);DSA / MoE / 量化 / 并行的完整机理与图示见 [deepseek-v3.2-exp](deepseek_v3_2_exp.md),此处记 GLM-5 的规格差异与部署配置。 |
| 140 | - **355B 变体**:官方称 GLM-5 从 355B-A32B 扩展到 744B-A40B,但 HF 仅发布 744B/40B 检查点(`zai-org/GLM-5` + `zai-org/GLM-5-FP8`);355B-A32B 未作为独立 HF 仓发布。`zai-org/GLM-5.1` 是另一发布检查点,不别名到本文件。 | 94 | - **355B 变体**:官方称 GLM-5 从 355B-A32B 扩展到 744B-A40B,但 HF 仅发布 744B/40B 检查点(`zai-org/GLM-5` + `zai-org/GLM-5-FP8`);355B-A32B 未作为独立 HF 仓发布。`zai-org/GLM-5.1` 是另一发布检查点,不别名到本文件。 |
| 141 | 95 | ||
| 96 | +## 部署与证据入口 | ||
| 97 | + | ||
| 98 | +- [模型部署与验收流程](../../guides/models/glm_5_deployment.md):配置、开关、失效条件与回退入口。 | ||
| 99 | +- [GLM-5 A3 W8A8 Decode 的来源性能观测](../../optimizations/benchmark_observations/glm5_a3_source_benchmark.md):固定来源自报,未经本仓独立复测。 | ||
| 100 | + | ||
| 142 | ## Source basis | 101 | ## Source basis |
| 143 | 102 | ||
| 144 | cann-recipes-infer @ **2c45117**(Atlas A3 部署事实): | 103 | cann-recipes-infer @ **2c45117**(Atlas A3 部署事实): |
| @@ -167,4 +126,4 @@ cann-recipes-infer @ **2c45117**(Atlas A3 部署事实): | |||
| 167 | - [npu-graph-mode](../techniques/npu_graph_mode.md) | 126 | - [npu-graph-mode](../techniques/npu_graph_mode.md) |
| 168 | - [weight-nz](../techniques/weight_nz.md) | 127 | - [weight-nz](../techniques/weight_nz.md) |
| 169 | - [atlas-a3](../platforms/atlas_a3.md) | 128 | - [atlas-a3](../platforms/atlas_a3.md) |
| 170 | -- [indexer-prolog](../kernels/indexer_prolog.md)(GLM-5 indexer 前处理 Q/Weight 多流切流 "22"/"33") | 129 | +- [indexer-prolog](../kernels/indexer_prolog.md)(GLM-5 indexer 前处理 Q/Weight 多流切流 "22"/"33") |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: GPT-OSS | 3 | title: GPT-OSS |
| 4 | -description: GPT-OSS 是 OpenAI 开源的 MoE 架构大语言模型家族,包含 **GPT-OSS-20B** 与 **GPT-OSS-120B** 两个规格。cann-recipes-infer 基于 HuggingFace Transformers 官方 `modeling_gpt_oss.py`,在 **Atlas A2 系列产品**上实现了单机单 batch 推理样例:GPT-OSS-20B 单 device 部署,GPT-OSS-120B 8 卡 TP 部署。 优化手段为经典"大 MoE 模型 NPU 推理"组合拳:Attention TP + MoE TP(`gate_up_proj` 合并)+ 固定 KV Cache + 融合算子(RMSNorm / AddRMSNorm / `fused_infer_attention_score_v2` / MoE routing 四件套 / GroupedMatmul)+ 集合通信 AIV 展开。**本样例仅支持 eager 单算子模式 + batch_size=1**,暂未引入图模式 / MTP / EP / W8A8 等更激进的优化。 | 4 | +description: GPT-OSS 的 GQA、可学习 attention sink、滑窗与带 bias 的 clamped GLU 专家机制。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,28 +14,20 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/gpt-oss/gpt_oss_optimization.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/gpt-oss/gpt_oss_optimization.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 16 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 17 | +updated_at: '2026-10-21T10:02:02Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a2 | 19 | - a2 |
| 20 | --- | 20 | --- |
| 21 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 22 | # GPT-OSS | 24 | # GPT-OSS |
| 23 | 25 | ||
| 24 | ## Summary | 26 | ## Summary |
| 25 | 27 | ||
| 26 | -GPT-OSS 是 OpenAI 开源的 MoE 架构大语言模型家族,包含 **GPT-OSS-20B** 与 **GPT-OSS-120B** 两个规格。cann-recipes-infer 基于 HuggingFace Transformers 官方 `modeling_gpt_oss.py`,在 **Atlas A2 系列产品**上实现了单机单 batch 推理样例:GPT-OSS-20B 单 device 部署,GPT-OSS-120B 8 卡 TP 部署。 | 28 | +GPT-OSS包含20B/120B规格,使用GQA、可学习attention sink和路由MoE;专家带bias,激活为clamped GLU,不等于普通SwiGLU。 |
| 27 | 29 | ||
| 28 | -优化手段为经典"大 MoE 模型 NPU 推理"组合拳:Attention TP + MoE TP(`gate_up_proj` 合并)+ 固定 KV Cache + 融合算子(RMSNorm / AddRMSNorm / `fused_infer_attention_score_v2` / MoE routing 四件套 / GroupedMatmul)+ 集合通信 AIV 展开。**本样例仅支持 eager 单算子模式 + batch_size=1**,暂未引入图模式 / MTP / EP / W8A8 等更激进的优化。 | 30 | +本卡保留模型结构,A2样例的权重转换、batch/eager/TP限制由部署Guide维护。平台配置、开关和验收步骤见[模型部署流程](../../guides/models/gpt_oss_deployment.md),此处只保留模型机制。 |
| 29 | - | ||
| 30 | -### Highlights | ||
| 31 | - | ||
| 32 | -- **Atlas A2 目标平台**(与仓内多数走 Atlas A3 的模型不同) | ||
| 33 | -- **权重仅支持 bf16**:原始 mxfp4 权重需离线转换为 bf16 后才能喂入本样例 | ||
| 34 | -- **parallel_config 三项同值约束**:`attn_tp_size == moe_tp_size == lmhead_tp_size` | ||
| 35 | -- **可学习 attention sink**:每头一个 `self.sinks` 标量经 FIA `learnable_sink` 入参生效,按 rank 切分;GPT-OSS 标志性注意力设计 | ||
| 36 | -- **专家走 clamped-GLU**(`limit=7.0` / `alpha=1.702` / `(up+1)*glu`),两个 GMM 均带 bias,非裸 SwiGLU | ||
| 37 | -- **`scatter_update` 管 KV Cache 更新**,`npu_fused_infer_attention_score_v2` 统一 prefill/decode | ||
| 38 | -- **`npu_moe_gating_top_k_softmax` + `npu_moe_init_routing` + `npu_moe_compute_expert_tokens` + `npu_moe_finalize_routing` + `npu_grouped_matmul`** 完整 MoE 融合算子链 | ||
| 39 | 31 | ||
| 40 | ## Architecture And Serving Traits | 32 | ## Architecture And Serving Traits |
| 41 | 33 | ||
| @@ -60,89 +52,15 @@ flowchart TD | |||
| 60 | - **MoE**:routed experts,无 shared expert。逻辑上是 gate / up / down 三矩阵,但物理实现已将 gate 与 up 合并为单个 `gate_up_proj`(带 bias),与 `down_proj`(带 bias)共两个 GMM;激活为带 clamp 的 GLU 变体而非裸 SwiGLU,详见下文。 | 52 | - **MoE**:routed experts,无 shared expert。逻辑上是 gate / up / down 三矩阵,但物理实现已将 gate 与 up 合并为单个 `gate_up_proj`(带 bias),与 `down_proj`(带 bias)共两个 GMM;激活为带 clamp 的 GLU 变体而非裸 SwiGLU,详见下文。 |
| 61 | - **支持滑窗注意力**:`config.layer_types[layer_idx] == "sliding_attention"` 的层走 `sliding_window`,对应 FIA 的 `sparse_mode=4`;非滑窗层 `sparse_mode=3`。属仓内带 attention-sink + 滑窗的非裸 MoE LLM 结构。 | 53 | - **支持滑窗注意力**:`config.layer_types[layer_idx] == "sliding_attention"` 的层走 `sliding_window`,对应 FIA 的 `sparse_mode=4`;非滑窗层 `sparse_mode=3`。属仓内带 attention-sink + 滑窗的非裸 MoE LLM 结构。 |
| 62 | 54 | ||
| 63 | -### 支持的部署规格 | 55 | +## 部署与证据入口 |
| 64 | 56 | ||
| 65 | -| 配置文件 | 模型 | world_size | attn_tp / moe_tp / lmhead_tp | exe_mode | dataset | | 57 | +- [模型部署与验收流程](../../guides/models/gpt_oss_deployment.md):配置、开关、失效条件与回退入口。 |
| 66 | -| ---- | ---- | ---- | ---- | ---- | ---- | | ||
| 67 | -| `gpt_oss_20b.yaml` | GPT-OSS-20B | 1 | 1 / 1 / 1 | eager | default | | ||
| 68 | -| `gpt_oss_120b_8tp.yaml` | GPT-OSS-120B | 8 | 8 / 8 / 8 | eager | LongBench | | ||
| 69 | - | ||
| 70 | -> `parallel_config` 要求 `attn_tp_size`、`moe_tp_size`、`lmhead_tp_size` **三者相同**(见 config YAML 注释)。仅提供 TP 切分,不提供 EP 路径。 | ||
| 71 | - | ||
| 72 | -### 约束 | ||
| 73 | - | ||
| 74 | -- **batch_size = 1**:`infer.sh` 与 executor 层面当前仅支持单 batch。 | ||
| 75 | -- **eager 单算子模式**:`exe_mode` 枚举只有 `eager`,未支持图模式 / cache_compile。 | ||
| 76 | -- **权重格式**:原始 mxfp4 权重**不能直接用**,需参考 [openai/gpt-oss weights.py](https://github.com/openai/gpt-oss/blob/7b583341fe16729127f6d5b94a7b09ccae97e1a1/gpt_oss/torch/weights.py) 自行离线转换为 bf16。 | ||
| 77 | -- **在线切分**:YAML 默认 `enable_online_split_weight: True`,加载时在线切到各卡,不需离线切分。 | ||
| 78 | - | ||
| 79 | -## Known Optimizations | ||
| 80 | - | ||
| 81 | -优化要点见下文。 | ||
| 82 | - | ||
| 83 | -### Attention Tensor Parallel (TP) 优化 | ||
| 84 | - | ||
| 85 | -对 Attention 的张量切分策略可以分为**对 QKV 头的切分**和**对线性层的切分**。 | ||
| 86 | - | ||
| 87 | -在对 QKV 头切分时,attention 的多头计算机制可以方便进行张量切分,每个头先独立计算,再将结果 concat 起来。假设模型的 attention 层需要对 `num_heads` 个 query 按照切分数量 `tp_size` 进行切分,要求 `num_heads` 必须能被 `tp_size` 整除,每张卡放置 query 头个数为 `num_heads_per_rank = num_heads // tp_size`;key 和 value 头数相等,且可能小于等于 query 头个数(在 MQA 和 GQA 的场景下会小于)。为了确保每张卡至少放置一个 key 和 value 头,每张卡放置的 key 或 value 头数计算方法为 `num_key_value_heads_per_rank = max(num_key_value_heads // tp_size, 1)`。QKV 头在卡上排布情况如下图所示。 | ||
| 88 | - | ||
| 89 | - | ||
| 90 | -*来源:[`docs/models/gpt-oss/figures/attention_tp.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/gpt-oss/figures/attention_tp.png)(cann-recipes-infer @ 2f82d10)* | ||
| 91 | - | ||
| 92 | -针对 `q_proj`、`k_proj` 和 `v_proj` 三个线性层,可以进一步**合并为一个线性层 `qkv_proj`**,即在推理中将三个矩阵乘替换为一个矩阵乘,可以最大化使用 NPU 的计算能力提升性能。随后将结果切分得到 Q、K、V,并进行 attention 计算,最后通过 `o_proj` 层输出。 | ||
| 93 | - | ||
| 94 | -### MoE Tensor Parallel 优化 | ||
| 95 | - | ||
| 96 | -假设模型的 MoE 层的切分数量为 `tp_size`,专家个数为 `expert_num`。对 MoE 层进行张量切分时: | ||
| 97 | - | ||
| 98 | -- 对 `gate_proj` 与 `up_proj` 进行**列切分** | ||
| 99 | -- 对 `down_proj` 进行**行切分** | ||
| 100 | -- 对 `gate_proj` 和 `up_proj` 可以采取**合并 matmul** 的优化方式,得到 `gate_up_proj`,实现计算性能优化 | ||
| 101 | - | ||
| 102 | -> **专家激活是带 clamp 的 GLU 变体,非裸 SwiGLU**:`GptOssExperts` 中 `gate_up_proj` 合并输出按 `[..., ::2]` / `[..., 1::2]` 拆出 `gate`、`up` 后,先 `gate.clamp(max=7.0)`、`up.clamp(-7.0, 7.0)`(`self.limit = 7.0`),再 `glu = gate * sigmoid(gate * 1.702)`(`self.alpha = 1.702`),最终送入第二个 GMM 的张量是 `(up + 1) * glu`。两个 `npu_grouped_matmul`(`gate_up_proj`、`down_proj`)均带 bias(`gate_up_proj_bias` / `down_proj_bias`)。这些 clamp 上下界、`alpha` 缩放与 `+1` 偏移会直接影响数值正确性,权重转换/对齐时不可按普通 SwiGLU 处理。 | ||
| 103 | - | ||
| 104 | -### 固定 KV Cache 大小 | ||
| 105 | - | ||
| 106 | -- 首先申请一块**固定大小**的 KV Cache tensor,以优化源码动态申请内存的性能损失。KV Cache 的大小为 `(batch * seq_len * num_key_value_heads_per_rank * head_dim)`,其中 `seq_len` 至少为输入和输出的最大总和,以保证在推理过程中 KV Cache 的更新不会越界。 | ||
| 107 | - > `gpt_oss_optimization.md` 称该实现位于 `GptOssForCausalLM.init_cache`,但 modeling 里 `GptOssForCausalLM` 仅有 `__init__` / `forward` / `load_weights`,并无 `init_cache` 方法。实际由 executor runner 的 `init_kvcache` 流程在 device 上预分配:模型侧 `GptOssAttention` 仅占位 `self.k_cache = self.v_cache = torch.Tensor([]) # will be set in runner's init_kvcache`,由 `model_worker._init_kvcache` 完成分配(`init_cache` 只是框架对 `hasattr(self.model, "init_cache")` 的可选回调,本模型未实现)。 | ||
| 108 | -- 在计算完 Q、K、V 后,可以通过 [`scatter_update`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-scatter_update.md) 算子对指定位置上的 KV Cache 进行更新。 | ||
| 109 | - | ||
| 110 | -### 使能融合算子 | ||
| 111 | - | ||
| 112 | -#### RMSNorm 融合与 AddRMSNorm 融合 | ||
| 113 | - | ||
| 114 | -替换 `GptOssRMSNorm` 的 `forward` 函数,使用融合算子接口 [`torch_npu.npu_rms_norm`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/%EF%BC%88beta%EF%BC%89torch_npu-npu_rms_norm.md) 和 `torch_npu.npu_add_rms_norm`,将多个算子的计算替换成融合 kernel,提升性能。 | ||
| 115 | - | ||
| 116 | -#### GMM 使能与 Routing 优化 | ||
| 117 | - | ||
| 118 | -在 GPT-OSS 的 MoE 专家模块中,通过使能融合算子,进一步提升性能,可以参考 `GptOssMLP` 类中的实现。 | ||
| 119 | - | ||
| 120 | -- **快速选择专家**:使用 [`torch_npu.npu_moe_gating_top_k_softmax`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_gating_top_k_softmax.md) 融合算子,代替原来"先 topk 再 softmax"的多算子操作,更快速地计算出 token 和专家的分数。 | ||
| 121 | -- **高效排序和 token 路由**: | ||
| 122 | - - [`torch_npu.npu_moe_init_routing`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_init_routing.md) —— 获取专家的排序; | ||
| 123 | - - [`torch_npu.npu_moe_compute_expert_tokens`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_compute_expert_tokens.md) —— 获取每个专家需要计算的 token 数; | ||
| 124 | - - [`torch_npu.npu_moe_finalize_routing`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_finalize_routing.md) —— 将专家计算完成后的 token 重新排布并加权求和,获得最终输出。 | ||
| 125 | -- **高性能专家计算**:[`torch_npu.npu_grouped_matmul`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_grouped_matmul.md) 融合算子,实现多个专家的矩阵乘计算,提高计算和搬运效率。 | ||
| 126 | - | ||
| 127 | -### Flash Attention 融合算子优化 | ||
| 128 | - | ||
| 129 | -通过使能 [`torch_npu.npu_fused_infer_attention_score_v2`](https://www.hiascend.com/document/detail/zh/Pytorch/720/apiref/torchnpuCustomsapi/context/torch_npu-npu_fused_infer_attention_score_v2.md) 推理场景下支持图模式的 FlashAttention 算子,既可以支持**全量计算场景(prefill)**,也可支持**增量计算场景(decode)**。 | ||
| 130 | - | ||
| 131 | -> 与 `npu_fused_infer_attention_score`(v1)的差异详见 [fused-infer-attention-score](../kernels/fused_infer_attention_score.md)。 | ||
| 132 | - | ||
| 133 | -### 集合通信使能 AIV 展开 | ||
| 134 | - | ||
| 135 | -利用 Device 的 Vector Core 计算单元来加速 AllReduce 操作,可通过环境变量 [`HCCL_OP_EXPANSION_MODE`](https://www.hiascend.com/document/detail/zh/CANNCommunityEdition/83RC1alpha002/maintenref/envvar/envref_07_0096.html) 使能 AIV,配置如下: | ||
| 136 | - | ||
| 137 | -```shell | ||
| 138 | -export HCCL_OP_EXPANSION_MODE=AIV | ||
| 139 | -``` | ||
| 140 | 58 | ||
| 141 | ## Evidence | 59 | ## Evidence |
| 142 | 60 | ||
| 143 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/gpt-oss/gpt_oss_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/gpt-oss/gpt_oss_optimization.md)(Attention TP / MoE TP / 固定 KV Cache / 融合算子 / FA / AIV 六大章节) | 61 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/gpt-oss/gpt_oss_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/gpt-oss/gpt_oss_optimization.md)(Attention TP / MoE TP / 固定 KV Cache / 融合算子 / FA / AIV 六大章节) |
| 144 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/README.md)(Atlas A2 环境准备、权重转换约束、单 batch 限制、在线切分默认开启) | 62 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/README.md)(Atlas A2 环境准备、权重转换约束、单 batch 限制、在线切分默认开启) |
| 145 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/config/gpt_oss_{20b,120b_8tp}.yaml`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/config/gpt_oss_{20b,120b_8tp}.yaml)(两份部署模板) | 63 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/config/gpt_oss_{20b,120b_8tp}.yaml`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/config/gpt_oss_20b.yaml)(两份部署模板) |
| 146 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/models/modeling_gpt_oss.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/models/modeling_gpt_oss.py)(`GptOssAttention.sinks` 可学习 sink + `learnable_sink` 入参 / `GptOssExperts` clamped-GLU 带 bias / `GptOssAttention` k_cache 占位待 runner 分配 / `GptOssMLP` 参考实现) | 64 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/gpt_oss/models/modeling_gpt_oss.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/gpt_oss/models/modeling_gpt_oss.py)(`GptOssAttention.sinks` 可学习 sink + `learnable_sink` 入参 / `GptOssExperts` clamped-GLU 带 bias / `GptOssAttention` k_cache 占位待 runner 分配 / `GptOssMLP` 参考实现) |
| 147 | 65 | ||
| 148 | ## Links | 66 | ## Links |
| @@ -150,4 +68,4 @@ export HCCL_OP_EXPANSION_MODE=AIV | |||
| 150 | - [atlas-a3](../platforms/atlas_a3.md) — 本样例目标为 Atlas A2,但 cann-recipes-infer 主体面向 A3 | 68 | - [atlas-a3](../platforms/atlas_a3.md) — 本样例目标为 Atlas A2,但 cann-recipes-infer 主体面向 A3 |
| 151 | - [tensor-parallel](../parallelism/tensor_parallel.md) | 69 | - [tensor-parallel](../parallelism/tensor_parallel.md) |
| 152 | - [fused-infer-attention-score](../kernels/fused_infer_attention_score.md) | 70 | - [fused-infer-attention-score](../kernels/fused_infer_attention_score.md) |
| 153 | -- [ascendc](../frameworks/ascendc.md) | 71 | +- [ascendc](../frameworks/ascendc.md) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: HSTU | 3 | title: HSTU |
| 4 | -description: HSTU(Hierarchical Sequential Transduction Unit)是 Meta 论文《Actions Speak Louder Than Words》提出的**生成式推荐(Generative Recommendation, GR)架构**,把推荐视为生成建模问题,以应对高基数、非平稳的数据流,同时**统一支持检索与排序**。基于 HSTU 的超大模型(1.5 万亿参数)在线 A/B 指标提升 **12.4%**,已在 Meta 多平台部署。 cann-recipes-infer 基于 NVIDIA recsys-examples `v25.11` 仓库进行 NPU 适配,替换了 GPU 算子与 tensorrt-llm KV Cache 管理逻辑,在 Atlas A2 系列产品上实现了 HSTU 单卡与多卡推理。与 LLM 模型不同,**HSTU 是生成式推荐模型**:输入是用户行为/物品的离散 ID 序列(jagged tensor),模型容量主要在**超大 Embedding 表**上,dense 部分相对较小。 | 4 | +description: HSTU 生成式推荐的离散 ID、jagged 序列、稀疏 Embedding 容量与推荐缓存机制。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,28 +14,20 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hstu/hstu_optimization.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hstu/hstu_optimization.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 16 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-06-27T00:00:00Z' | 17 | +updated_at: '2026-10-21T10:02:02Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a2 | 19 | - a2 |
| 20 | --- | 20 | --- |
| 21 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 22 | # HSTU | 24 | # HSTU |
| 23 | 25 | ||
| 24 | ## Summary | 26 | ## Summary |
| 25 | 27 | ||
| 26 | -HSTU(Hierarchical Sequential Transduction Unit)是 Meta 论文《Actions Speak Louder Than Words》提出的**生成式推荐(Generative Recommendation, GR)架构**,把推荐视为生成建模问题,以应对高基数、非平稳的数据流,同时**统一支持检索与排序**。基于 HSTU 的超大模型(1.5 万亿参数)在线 A/B 指标提升 **12.4%**,已在 Meta 多平台部署。 | 28 | +HSTU(Hierarchical Sequential Transduction Unit)面向生成式推荐,将用户、物品与上下文的离散ID序列用于检索和排序。其容量侧重稀疏Embedding,序列使用jagged形态,不能照搬文本LLM的词表、KV与并行假设。 |
| 27 | 29 | ||
| 28 | -cann-recipes-infer 基于 NVIDIA [recsys-examples](https://github.com/NVIDIA/recsys-examples) `v25.11` 仓库进行 NPU 适配,替换了 GPU 算子与 tensorrt-llm KV Cache 管理逻辑,在 Atlas A2 系列产品上实现了 HSTU 单卡与多卡推理。与 LLM 模型不同,**HSTU 是生成式推荐模型**:输入是用户行为/物品的离散 ID 序列(jagged tensor),模型容量主要在**超大 Embedding 表**上,dense 部分相对较小。 | 30 | +原总览引用的论文业务A/B数字没有本NPU样例的对应实验,不作为本卡部署收益。平台配置、开关和验收步骤见[模型部署流程](../../guides/models/hstu_deployment.md),此处只保留模型机制。 |
| 29 | - | ||
| 30 | -### Highlights | ||
| 31 | - | ||
| 32 | -- **Atlas A2 系列**单卡/多卡推理,依赖 RecSDK-Torch Docker 镜像 + CANN 8.5.0 + nnal 加速库 | ||
| 33 | -- **KV Cache 管理 NPU 化**:`kv_cache_manager_impl` 替代 tensorrt-llm 原实现 | ||
| 34 | -- **三个 NPU 融合算子适配**:`hstu_paged`(HSTU 自研 attention 算子)、`in_linear_silu`、`concat_nd_jagged`(`concat_2d_jagged` 的 n 路扩展) | ||
| 35 | -- **KV Cache layout 重排**:`[num_pages, num_layers, 2, block_size]` → `[num_layers, 2, num_pages, block_size]`,引入 `_npu_reshape_and_cache` 加速 append | ||
| 36 | -- **分布式推理**:[embedding-tp](../parallelism/embedding_tp.md) + DP,`--embed_tp_size` 控制 | ||
| 37 | -- **KV Offload 与 HSTU dense 计算并行**:见 [kvcache-offload](../techniques/kvcache_offload.md)(HSTU 变体) | ||
| 38 | -- **ACLGraph 使能**:单算子模式 + 图模式均支持 KV Offload 并行 | ||
| 39 | 31 | ||
| 40 | ## Architecture And Serving Traits | 32 | ## Architecture And Serving Traits |
| 41 | 33 | ||
| @@ -69,151 +61,9 @@ flowchart TD | |||
| 69 | Kcache -.->|每层 Attention 完成即触发| Offload | 61 | Kcache -.->|每层 Attention 完成即触发| Offload |
| 70 | ``` | 62 | ``` |
| 71 | 63 | ||
| 72 | -### 推理执行入口与规格 | 64 | +## 部署与证据入口 |
| 73 | 65 | ||
| 74 | -- 入口脚本:`models/hstu/inference/inference_gr_ranking.py` | 66 | +- [模型部署与验收流程](../../guides/models/hstu_deployment.md):配置、开关、失效条件与回退入口。 |
| 75 | -- gin config:`models/hstu/inference/configs/kuairand_1k_inference_ranking.gin` | ||
| 76 | -- 推理模式: | ||
| 77 | - - **eval 模式**:`--mode eval --enable_fused_ops all` | ||
| 78 | - - **simulate 模式**:`--mode simulate --enable_fused_ops all` | ||
| 79 | - - **profiling**:追加 `--enable_profiler` | ||
| 80 | -- 数据集:`kuairand-1k`(`python3 ./preprocessor.py --dataset_name "kuairand-1k" --inference` 预处理) | ||
| 81 | - | ||
| 82 | -单机两卡分布式推理示例: | ||
| 83 | - | ||
| 84 | -```bash | ||
| 85 | -ASCEND_RT_VISIBLE_DEVICES=0,1 \ | ||
| 86 | -torchrun --rdzv-backend=c10d --rdzv-endpoint=localhost:6000 \ | ||
| 87 | - --nnodes=1 --nproc-per-node=2 \ | ||
| 88 | - ./inference/inference_gr_ranking.py \ | ||
| 89 | - --gin_config_file ./inference/configs/kuairand_1k_inference_ranking.gin \ | ||
| 90 | - --embed_tp_size 2 --mode eval --enable_fused_ops all | ||
| 91 | -``` | ||
| 92 | - | ||
| 93 | -### 外部依赖 | ||
| 94 | - | ||
| 95 | -- **recsys-examples v25.11**:HSTU 参考实现(GPU 路径),NPU 基于此适配 | ||
| 96 | -- **RecSDK**(gitcode.com/Ascend/RecSDK):算子库(`concat_2d_jagged`、`in_linear_silu` 等) | ||
| 97 | -- **nnal 加速库**:`_npu_reshape_and_cache` 依赖 | ||
| 98 | -- **catlass v1.3.0**:`in_linear_silu` 算子依赖 CATLASS 源码(通过 `CATLASS_HOME` 环境变量指向) | ||
| 99 | -- **hstu_cann.patch**:对 recsys-examples 源码的 NPU 适配补丁 | ||
| 100 | -- `torchrec==1.1.0` + `fbgemm_npu`(通过 `LIB_FBGEMM_NPU_API_SO_PATH` 指向) | ||
| 101 | - | ||
| 102 | -## Known Optimizations | ||
| 103 | - | ||
| 104 | -来源文档见 Evidence 中的 `hstu_optimization.md`。 | ||
| 105 | - | ||
| 106 | -### NPU 模型适配 | ||
| 107 | - | ||
| 108 | -我们在 Nvidia Recsys-Examples 仓库的基础上,实现了 **GPU 算子的 Torch 小算子替换**,以及 KV Cache 的管理逻辑,实现了 NPU 的 `kv_cache_manager_impl` 类,**代替了原有的 tensorrt-llm 框架实现逻辑**。 | ||
| 109 | - | ||
| 110 | -实现了 `kv_cache_manager_impl` 的以下方法,代码在 `models/hstu/modules/kv_cache_manager_impl.py` 文件中: | ||
| 111 | - | ||
| 112 | -- `allocate_pools` | ||
| 113 | -- `get_primary_pool` | ||
| 114 | -- `get_num_free_blocks` | ||
| 115 | -- `add_sequence_with_eviction` | ||
| 116 | -- `remove_sequence` | ||
| 117 | -- `evict_all_sequences` | ||
| 118 | -- `get_cached_start_position` | ||
| 119 | -- `get_num_tokens_cached` | ||
| 120 | -- `get_cache_block_ids` | ||
| 121 | - | ||
| 122 | -在小算子的适配逻辑上,适配了以下 torch 算子实现: | ||
| 123 | - | ||
| 124 | -- `jagged_2D_tensor_concat` | ||
| 125 | -- `torch_split_2D_jagged` | ||
| 126 | -- `torch_add_position_embeddings` | ||
| 127 | -- `torch_add_timestamp_positional_embeddings` | ||
| 128 | - | ||
| 129 | -此外,涉及到 GPU 的 **dynamic embedding** 用 `nn.Embedding` 来实现。 | ||
| 130 | - | ||
| 131 | -### NPU 算子适配 | ||
| 132 | - | ||
| 133 | -#### NPU `hstu_paged` 融合算子 | ||
| 134 | - | ||
| 135 | -在 HSTU attention 部分,Nvidia 实现了自己的 CUDA 算子,NPU 下已经有对应的融合算子,我们基于计算逻辑,实现了 `hstu_paged` 融合算子适配: | ||
| 136 | - | ||
| 137 | -```python | ||
| 138 | -# 来自 models/hstu/modules/paged_hstu_infer_layer.py | ||
| 139 | -jagged_attn_output = torch.ops.mxrec.hstu_paged( | ||
| 140 | - q=query, | ||
| 141 | - k=key, | ||
| 142 | - v=value, | ||
| 143 | - kv_cache=kv_cache_table, | ||
| 144 | - mask=None, | ||
| 145 | - attn_bias=None, | ||
| 146 | - mask_type=2, | ||
| 147 | - max_seq_len=self._max_seqlen, | ||
| 148 | - max_seq_len_k=jd.seqlen_k, | ||
| 149 | - silu_scale=1.0 / self._max_seqlen, | ||
| 150 | - seq_offset=jd.seqlen_offsets, | ||
| 151 | - seq_offset_k=kv_cache_metadata.total_history_offsets, | ||
| 152 | - seq_offset_t=jd.num_candidates_offsets, | ||
| 153 | - page_offsets=kv_cache_metadata.kv_indptr, | ||
| 154 | - page_ids=kv_cache_metadata.kv_indices, | ||
| 155 | - last_page_len=kv_cache_metadata.kv_last_page_len, | ||
| 156 | - num_target=jd.num_candidates, | ||
| 157 | - target_group_size=1) | ||
| 158 | -``` | ||
| 159 | - | ||
| 160 | -> HSTU attention 与 LLM 标准 Softmax Attention 的差异:含 `silu_scale`(SiLU 缩放因子,默认 `1/max_seq_len`)、支持 jagged 序列(`seqlen_offsets`)、支持候选物品分组(`num_candidates_offsets` + `target_group_size`)、paged KV 存储(`page_offsets` + `page_ids` + `last_page_len`)。 | ||
| 161 | - | ||
| 162 | -#### NPU `_npu_reshape_and_cache` 算子 | ||
| 163 | - | ||
| 164 | -在 kvcache 管理方面,**原始的 kv cache 的 layout 是 `[num_pages, num_layers, 2, block_size]`**,我们**将其修改为 `[num_layers, 2, num_pages, block_size]` 形式**,引入 `_npu_reshape_and_cache` 算子加速 `append_kv_cache` 的计算。 | ||
| 165 | - | ||
| 166 | -```python | ||
| 167 | -torch_npu._npu_reshape_and_cache(src_k, src_v, paged_k_cache, paged_v_cache, slot_i32) | ||
| 168 | -``` | ||
| 169 | - | ||
| 170 | -#### NPU `concat_nd_jagged` 算子 | ||
| 171 | - | ||
| 172 | -我们将 GPU 侧的 **Triton 融合算子 `triton_concat_2D_jagged`** 以及 **CUDA 融合算子 `jagged_2D_tensor_concat`** 统一替换为 NPU 侧的 `concat_nd_jagged`,实现同等功能的高效迁移。 | ||
| 173 | - | ||
| 174 | -`concat_nd_jagged` 算子在 RecSDK 团队开发的 `concat_2d_jagged` 算子基础上优化衍生,**原生算子仅支持 2 个 tensor 的 concat,不能完全满足 HSTU 模型的场景要求**;我们在 Torch 适配层完成增强改造,将能力扩展为支持 **n(n ≥ 2)个 tensor 的 jagged concat 场景**,从而覆盖更通用的融合拼接需求并降低算子分裂与维护成本。 | ||
| 175 | - | ||
| 176 | -具体增强实现(新增 `concat_nd_jagged_npu` 函数并注册到 `mxrec` 命名空间)见 `models/hstu/README.md` "相关依赖安装 - RecSDK 算子库安装"章节的 cpp 代码片段。 | ||
| 177 | - | ||
| 178 | -### 分布式推理(Embedding TP + DP) | ||
| 179 | - | ||
| 180 | -我们在推理侧新增了分布式推理能力,主要是 **Embedding Tensor Parallel (TP) 和 Data Parallel (DP)**。生成式推荐与普通 LLM 任务的最大不同在于: | ||
| 181 | - | ||
| 182 | -- **LLM**:参数主要集中在 dense Transformer 权重里,输入 token 来自于相对固定的文本词表; | ||
| 183 | -- **生成式推荐**:往往把用户、物品、上下文等离散特征统一建模为 token,模型容量**绝大部分来自超大规模的稀疏参数(Embedding Tables)**,词表规模可达千万甚至上亿。 | ||
| 184 | - | ||
| 185 | -基于此,我们在推理侧引入 Embedding TP(如下图),对大表**按 vocab 维度切分**到不同 NPU 上,以减少单卡显存负担,支撑模型推理。同时,在 Embedding TP 外采用 DP 进行并行处理,Embedding 部分进行 **DP → TP → DP** 的操作。 | ||
| 186 | - | ||
| 187 | - | ||
| 188 | -*来源:[`docs/models/hstu/figures/embedding_tp.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hstu/figures/embedding_tp.png)(cann-recipes-infer @ 2f82d10)* | ||
| 189 | - | ||
| 190 | -详见 [embedding-tp](../parallelism/embedding_tp.md)。 | ||
| 191 | - | ||
| 192 | -### KV Offload 与 HSTU dense 计算并行 | ||
| 193 | - | ||
| 194 | -将 KV offload 时机**前移**到每层 Attention 结束立即触发,放到另一条 NPU stream 异步执行,实现**计算链路(HSTU dense + MLP + 后处理)与通信链路(KV offload)的重叠**,显著降低流水线空泡。详见 [kvcache-offload](../techniques/kvcache_offload.md)(HSTU 变体)。 | ||
| 195 | - | ||
| 196 | -### 其他已使能的优化点 | ||
| 197 | - | ||
| 198 | -README "优化点介绍"章节列出的额外优化: | ||
| 199 | - | ||
| 200 | -- TensorRT-LLM 库 KV Cache 管理 impl 方法的 NPU 适配替换(见 NPU 模型适配) | ||
| 201 | -- 使用 torch 小算子替换 Triton 和 CUDA 算子(见 NPU 模型适配) | ||
| 202 | -- **KVCache 多级缓存、onload 和 offload 流程适配与优化** | ||
| 203 | -- **高性能融合算子接入**(`hstu_paged`、`in_linear_silu`、`concat_nd_jagged`) | ||
| 204 | -- **ACLGraph 使能** | ||
| 205 | -- KV Cache layout 优化,引入 `_npu_reshape_and_cache` 算子(见 NPU `_npu_reshape_and_cache` 算子) | ||
| 206 | -- 分布式推理功能,在 Embedding Table 占用显存大的情况下,支持更大的 dense 参数模型推理(见 分布式推理) | ||
| 207 | -- **使能单算子模式和图模式下的 KV Offload 与 dense 层计算并行,实现通算掩盖**(见 KV Offload 与 HSTU dense 计算并行) | ||
| 208 | - | ||
| 209 | -### 其他优化方向(TODO) | ||
| 210 | - | ||
| 211 | -源文档列出的后续方向: | ||
| 212 | - | ||
| 213 | -- 使用融合算子对前处理部分的 `concat_2d_jagged` 进行处理 | ||
| 214 | -- 使能 HSTU dense 模块计算与 offload 并行 | ||
| 215 | -- 完成更多融合算子的开发和接入,以进一步降低推理时延 | ||
| 216 | -- 在 embedding 处理和 offload 流程上通过尝试使能 **shmen** 等手段进一步提升性能 | ||
| 217 | 67 | ||
| 218 | ## Evidence | 68 | ## Evidence |
| 219 | 69 | ||
| @@ -227,4 +77,4 @@ README "优化点介绍"章节列出的额外优化: | |||
| 227 | - [embedding-tp](../parallelism/embedding_tp.md) | 77 | - [embedding-tp](../parallelism/embedding_tp.md) |
| 228 | - [data-parallel](../parallelism/data_parallel.md) | 78 | - [data-parallel](../parallelism/data_parallel.md) |
| 229 | - [kvcache-offload](../techniques/kvcache_offload.md)(HSTU 变体) | 79 | - [kvcache-offload](../techniques/kvcache_offload.md)(HSTU 变体) |
| 230 | -- [ascendc](../frameworks/ascendc.md) | 80 | +- [ascendc](../frameworks/ascendc.md) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: HunyuanImage-3.0 | 3 | title: HunyuanImage-3.0 |
| 4 | -description: HunyuanImage-3.0 是腾讯混元发布的 MoE + DiT 文生图模型,cann-recipes-infer 提供 Atlas A2/A3 上的 TP 推理方案,并在其上叠加 **Attention TP、MoE TP、MoE EP(DoubleRouting)、MoE 多流、VAE 并行、CFG 并行、通算融合(AllGatherMM / MMReduceScatter)** 以及若干冗余 cast 消除。关键优化与相应源图并存,结构化拓扑用 mermaid 重绘。 | 4 | +description: HunyuanImage-3.0 的 DiT、MoE 与图像生成模块;并行、融合和部署差异分别引用任务入口。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,17 +14,21 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 16 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 17 | +updated_at: '2026-10-21T10:02:02Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a2 | 19 | - a2 |
| 20 | - a3 | 20 | - a3 |
| 21 | --- | 21 | --- |
| 22 | 22 | ||
| 23 | + | ||
| 24 | + | ||
| 23 | # HunyuanImage-3.0 | 25 | # HunyuanImage-3.0 |
| 24 | 26 | ||
| 25 | ## Summary | 27 | ## Summary |
| 26 | 28 | ||
| 27 | -HunyuanImage-3.0 是腾讯混元发布的 MoE + DiT 文生图模型,cann-recipes-infer 提供 Atlas A2/A3 上的 TP 推理方案,并在其上叠加 **Attention TP、MoE TP、MoE EP(DoubleRouting)、MoE 多流、VAE 并行、CFG 并行、通算融合(AllGatherMM / MMReduceScatter)** 以及若干冗余 cast 消除。关键优化与相应源图并存,结构化拓扑用 mermaid 重绘。 | 29 | +HunyuanImage-3.0 是MoE与DiT结合的文生图模型,去噪网络、文本输入和VAE解码承担不同职责。Attention、专家与CFG等并行维度来自不同模块,不能视为一组天然兼容的默认开关。 |
| 30 | + | ||
| 31 | +平台配置、开关和验收步骤见[模型部署流程](../../guides/models/hunyuan_image_3_deployment.md),此处只保留模型机制。 | ||
| 28 | 32 | ||
| 29 | ## Architecture And Serving Traits | 33 | ## Architecture And Serving Traits |
| 30 | 34 | ||
| @@ -36,142 +40,14 @@ HunyuanImage-3.0 是腾讯混元发布的 MoE + DiT 文生图模型,cann-recip | |||
| 36 | - `models/hunyuan-image-3.0/utils/` — 工具 | 40 | - `models/hunyuan-image-3.0/utils/` — 工具 |
| 37 | - `module/vae_patch_parallel.py` — VAE 并行(共享) | 41 | - `module/vae_patch_parallel.py` — VAE 并行(共享) |
| 38 | 42 | ||
| 39 | -### 统一推理拉起 | 43 | +## 部署与证据入口 |
| 40 | 44 | ||
| 41 | -部署经统一拉起架构 [mm-inference-launch](../frameworks/mm_inference_launch.md):`models/hunyuan-image-3.0/infer.sh` + YAML 场景切换,`entry_script` 为 `run_image_gen.py`,启动器默认 `torchrun`。config 变体(`config/`):`ep8_cfg`(`world_size=16`,Attention TP8 + MoE EP8(`moe-ep: true`)+ CFG 并行 + VAE 并行)。 | 45 | +- [模型部署与验收流程](../../guides/models/hunyuan_image_3_deployment.md):配置、开关、失效条件与回退入口。 |
| 42 | - | ||
| 43 | -> 注:本段 `infer.sh` + `config/ep8_cfg.yaml` 统一拉起结构在正文主钉 @2f82d10 尚不存在(彼时启动脚本为 `demo.sh`,无 `config/`),系更晚的 **@2c45117** 引入;`ep8_cfg.yaml@2c45117` 字段:`world_size: 16`、`moe-ep: true`、`entry_script: run_image_gen.py`、`env_vars.CFG_PARALLEL=1` / `USE_VAE_PARALLEL=1`。 | ||
| 44 | - | ||
| 45 | -## Known Optimizations | ||
| 46 | - | ||
| 47 | -### 切分策略 | ||
| 48 | - | ||
| 49 | -#### Attention TP | ||
| 50 | - | ||
| 51 | -> 源:对 Attention 的张量切分策略可以分为对 QKV 头的切分和对线性层的切分。在对 QKV 头切分时,attention 的多头计算机制可以方便进行张量切分,每个头先独立计算,再将结果 concat 起来。 | ||
| 52 | - | ||
| 53 | -要求 `num_heads % attn_tp_size == 0`,每卡头数 | ||
| 54 | -`num_heads_per_rank = num_heads // attn_tp_size`;GQA/MQA 场景 K/V 头更少,需保证每卡至少 1 个 K/V 头: | ||
| 55 | -`num_key_value_heads_per_rank = max(num_key_value_heads // attn_tp_size, 1)`。`o_proj` 按行切分。 | ||
| 56 | - | ||
| 57 | - | ||
| 58 | -*来源:[`docs/models/hunyuan-image-3.0/figures/attention_tp.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/figures/attention_tp.png)(cann-recipes-infer @ 2f82d10)* | ||
| 59 | - | ||
| 60 | -**计算分解**:将 Q/K/V 的线性层合并为一次 Matmul(`merged_qkv_proj`)→ 拆分 Q/K/V → Q、V 归一化 + RoPE → `Fused_infer_attention_score` → `o_proj`。 | ||
| 61 | - | ||
| 62 | - | ||
| 63 | -*来源:[`docs/models/hunyuan-image-3.0/figures/attention_calcu.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/figures/attention_calcu.png)(cann-recipes-infer @ 2f82d10)* | ||
| 64 | - | ||
| 65 | -#### MoE TP | ||
| 66 | - | ||
| 67 | -> 源:假设模型的 MoE 层的切分数量为 `moe_tp_size`,专家个数为 expert_num。对 MoE 层进行张量切分。具体做法是对 `gate_proj` 与 `up_proj` 进行列切分,对 `down_proj` 进行行切分。同时对 `gate_proj` 与 `up_proj` 线性层采用合并计算的优化方式,得到 `w13_weight`。 | ||
| 68 | - | ||
| 69 | -每个专家层 `x = down(SiLU(gate(x)) * up(x))`;将切分后 `gate_proj` 与 `up_proj` concat 后使能 [`torch_npu.npu_swiglu`](https://www.hiascend.com/document/detail/zh/Pytorch/730/apiref/torchnpuCustomsapi/docs/context/overview.md) 融合算子(沿最后一维 chunk 为两块,计算 `SiLU(x[0]) * x[1]`)。 | ||
| 70 | - | ||
| 71 | - | ||
| 72 | -*来源:[`docs/models/hunyuan-image-3.0/figures/moe_opt.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/figures/moe_opt.png)(cann-recipes-infer @ 2f82d10)* | ||
| 73 | - | ||
| 74 | -#### MoE EP + DoubleRouting | ||
| 75 | - | ||
| 76 | -当 TP 切分下 routing / finalizing 开销占比不随 RANK 数下降时,改用 EP 切 S 轴,routing + finalizing 计算量变为 `1/ep_size`。MoE 走 DoubleRouting([double-routing](../techniques/double_routing.md) 的 HunyuanImage-3.0 场景),具体 8 步: | ||
| 77 | - | ||
| 78 | -1. GatingTopK 后使用 InitRouting 确定当前 RANK 的 token 分给哪些专家 | ||
| 79 | -2. all_to_all 发送 `tokens_per_expert` 至对应 RANK | ||
| 80 | -3. all_to_all 发送 tokens 至对应 RANK | ||
| 81 | -4. LocalRerouting:每个 RANK 内按专家序重排 | ||
| 82 | -5. GroupedMatMul:专家计算 | ||
| 83 | -6. LocalFinalizeRerouting:排序回第二次 all_to_all 后的排布 | ||
| 84 | -7. all_to_all 把计算完的 token 发送回原本 TP 域内对应 RANK | ||
| 85 | -8. FinalizeRerouting:对专家计算结果在 TP 域内累加 | ||
| 86 | - | ||
| 87 | - | ||
| 88 | -*来源:[`docs/models/hunyuan-image-3.0/figures/moe_double_routing.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/figures/moe_double_routing.png)(cann-recipes-infer @ 2f82d10)* | ||
| 89 | - | ||
| 90 | -为降低通信量,配合此方案:**q_proj 前 AllGather hidden_states + o_proj 后 ReduceScatter attn_output**,这样可为通算融合创造机会:AllGather × qkv_proj、o_proj × ReduceScatter(见下文)。 | ||
| 91 | - | ||
| 92 | -### 并行优化 | ||
| 93 | - | ||
| 94 | -#### MoE 多流并行 | ||
| 95 | - | ||
| 96 | -MoE 模块中,路由专家采用 TP 部署;共享专家的计算与路由专家完成 MoE 计算后的通信可通过多流并行掩盖,使用 `all_reduce` 异步机制。 | ||
| 97 | - | ||
| 98 | - | ||
| 99 | -*来源:[`docs/models/hunyuan-image-3.0/figures/moe_mlp_parallel.png`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/figures/moe_mlp_parallel.png)(cann-recipes-infer @ 2f82d10)* | ||
| 100 | - | ||
| 101 | -参见 [moe-multi-stream](../techniques/moe_multi_stream.md)。 | ||
| 102 | - | ||
| 103 | -#### VAE 并行(空间并行) | ||
| 104 | - | ||
| 105 | -把大尺寸图像在 H、W 维度切成 patch 分配到多 NPU 进程并行处理。卷积边界交换 / 注意力全局 gather / 插值扩展-裁剪。详见 [vae-patch-parallel](../parallelism/vae_patch_parallel.md) 与 [vae](../modules/vae.md)。 | ||
| 106 | - | ||
| 107 | -#### CFG 并行 | ||
| 108 | - | ||
| 109 | -`cond` / `uncond` 两样本拆到两卡并行,all_gather 后合并。示例:TP2 + CFGP: | ||
| 110 | - | ||
| 111 | -> 以 TP2+CFGP 为例,排布情况如下图所示。 | ||
| 112 | - | ||
| 113 | -详见 [cfg-parallel](../parallelism/cfg_parallel.md)。 | ||
| 114 | - | ||
| 115 | -### 使能融合算子 | ||
| 116 | - | ||
| 117 | -#### 通算融合(AllGatherMM / MMReduceScatter) | ||
| 118 | - | ||
| 119 | -EP 方案中,`qkv_proj` 前对 `hidden_states` 做 AllGather、`o_proj` 后做 ReduceScatter。串行会造成性能浪费,使用通算融合算子: | ||
| 120 | - | ||
| 121 | -- **AllGatherMM** — [`torch_npu.npu_all_gather_base_mm`](https://www.hiascend.com/document/detail/zh/Pytorch/730/apiref/torchnpuCustomsapi/docs/context/torch_npu-npu_all_gather_base_mm.md):仅支持 2D 输入、第 0 轴切分。调用前 `hidden_states` layout 由 `[b s h]` 转为 `[(s b) h]`,调用后再转回。**s 轴必须在最内侧**,否则精度异常。 | ||
| 122 | -- **MMReduceScatter** — [`torch_npu.npu_mm_reduce_scatter_base`](https://www.hiascend.com/document/detail/zh/Pytorch/730/apiref/torchnpuCustomsapi/docs/context/torch_npu-npu_mm_reduce_scatter_base.md):同样 2D 输入第 0 轴切分;`[b n s d]` → `[(s b) (n d)]` → 反转。**s 轴必须在最内侧**。该 MMReduceScatter 融合算子在 A2(Ascend910B)暂未使能:`o_proj` 后经 `torch.npu.get_device_name()` 检测到 `Ascend910B` 时走设备分支,回退普通 `dist.reduce_scatter`(matmul 与通信不融合);非 A2 才用 `npu_mm_reduce_scatter_base` 融合。AllGatherMM 侧无此分支(A2 仍直接调用融合算子)。 | ||
| 123 | - | ||
| 124 | -#### GroupedMatMul + Routing 优化 | ||
| 125 | - | ||
| 126 | -对 MoE,CANN 提供 `GroupedMatmul` 算子同时计算多个专家,避免 for 循环单独计算。具体实现在 `HunyuanMoE` 类的 `npu_grouped_matmul` 模式: | ||
| 127 | - | ||
| 128 | -- [`torch_npu.npu_moe_init_routing`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_init_routing.md) — MoE routing 计算,获取专家排序 | ||
| 129 | -- [`torch_npu.npu_moe_compute_expert_tokens`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_compute_expert_tokens.md) — 每个专家需要计算的 token 数 | ||
| 130 | -- [`torch_npu.npu_moe_finalize_routing`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_moe_finalize_routing.md) — 专家计算完成后的 token 重新排布 + 加权求和 | ||
| 131 | -- [`torch_npu.npu_grouped_matmul`](https://www.hiascend.com/document/detail/zh/Pytorch/710/apiref/torchnpuCustomsapi/context/torch_npu-npu_grouped_matmul.md) — 多个专家的矩阵乘 | ||
| 132 | - | ||
| 133 | -#### RmsNorm 算子优化 | ||
| 134 | - | ||
| 135 | -使能 [`torch_npu.npu_rms_norm`](https://www.hiascend.com/document/detail/zh/Pytorch/60RC2/apiref/apilist/ptaoplist_000140.html) 算子加速。 | ||
| 136 | - | ||
| 137 | -### 消除冗余计算 | ||
| 138 | - | ||
| 139 | -#### 消除旋转位置编码冗余 cast 算子 | ||
| 140 | - | ||
| 141 | -在 `HunyuanImage3ForCausalMM` 类中将 cos, sin 提前转成 bfloat16,消除 `apply_rotary_pos_emb` 中 cos/sin 从 float32 自动 cast 到 bfloat16 的冗余: | ||
| 142 | - | ||
| 143 | -```python | ||
| 144 | -cos = real_batched_index_select(cos, dim=1, idx=position_ids).to(torch.bfloat16) | ||
| 145 | -sin = real_batched_index_select(sin, dim=1, idx=position_ids).to(torch.bfloat16) | ||
| 146 | - | ||
| 147 | -if self.use_rotary_pos_emb: | ||
| 148 | - cos, sin = custom_pos_emb | ||
| 149 | - query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin) | ||
| 150 | -``` | ||
| 151 | - | ||
| 152 | -#### 消除 gate 计算冗余 cast 算子 | ||
| 153 | - | ||
| 154 | -屏蔽外层 `torch.autocast` 以保持 gate 的 float32 精度: | ||
| 155 | - | ||
| 156 | -```python | ||
| 157 | -with torch.npu.amp.autocast(enabled=False): | ||
| 158 | - if self.wg.weight.dtype == torch.float32: | ||
| 159 | - hidden_states = hidden_states.float | ||
| 160 | - logits = self.wg(hidden_states) | ||
| 161 | -``` | ||
| 162 | - | ||
| 163 | -#### 消除 FA 中 scale 的冗余计算 | ||
| 164 | - | ||
| 165 | -每次调用 FA 时默认在 CPU 计算 `scale=1/math.sqrt(d)`,导致下发中断。由于 `d` 在 config 里固定,将 `scale` 计算提前到 `HunyuanImage3Model.__init__` 存为成员变量,`flash_attn_func_npu` 复用。 | ||
| 166 | - | ||
| 167 | -#### 消除 timestep_index 的冗余计算 | ||
| 168 | - | ||
| 169 | -`gen_timestep_scatter_index[0, 0].item` 是强同步,造成 device 等待。将 `timestep_index` 的初始化提前至 `HunyuanImage3Text2ImagePipeline.__call__` 开头,并透传至 `casual_len` 的计算处,避免反复 Host2Device。 | ||
| 170 | 46 | ||
| 171 | ## Evidence | 47 | ## Evidence |
| 172 | 48 | ||
| 173 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md) | 49 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md) |
| 174 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/hunyuan-image-3.0/{run_image_gen.py, model_adaptor.py, adaptor_patches/, utils/}`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/hunyuan-image-3.0/{run_image_gen.py, model_adaptor.py, adaptor_patches/, utils/}) | 50 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/hunyuan-image-3.0/{run_image_gen.py, model_adaptor.py, adaptor_patches/, utils/}`](https://gitcode.com/cann/cann-recipes-infer/tree/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/hunyuan-image-3.0) |
| 175 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`module/vae_patch_parallel.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/module/vae_patch_parallel.py) | 51 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`module/vae_patch_parallel.py`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/module/vae_patch_parallel.py) |
| 176 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — 统一推理拉起结构(@2c45117,@2f82d10 尚为 `demo.sh`):[`models/hunyuan-image-3.0/infer.sh`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/infer.sh) + [`config/ep8_cfg.yaml`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/config/ep8_cfg.yaml)(`world_size: 16` / `moe-ep: true` / `entry_script: run_image_gen.py` / `CFG_PARALLEL=1` / `USE_VAE_PARALLEL=1`)+ [`config/README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/config/README.md) | 52 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — 统一推理拉起结构(@2c45117,@2f82d10 尚为 `demo.sh`):[`models/hunyuan-image-3.0/infer.sh`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/infer.sh) + [`config/ep8_cfg.yaml`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/config/ep8_cfg.yaml)(`world_size: 16` / `moe-ep: true` / `entry_script: run_image_gen.py` / `CFG_PARALLEL=1` / `USE_VAE_PARALLEL=1`)+ [`config/README.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/config/README.md) |
| 177 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — MMReduceScatter A2 设备分支(@2c45117,commit 59a7906 MR!405):[`adaptor_patches/hunyuan.py#L580-L607`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/adaptor_patches/hunyuan.py)(`torch.npu.get_device_name()` / `"Ascend910B"` 设备分支:A2 走 `dist.reduce_scatter`、非 A2 走 `npu_mm_reduce_scatter_base`)+ [`docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md)(MMReduceScatter 句尾「(A2平台下暂未使能该融合算子)」) | 53 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — MMReduceScatter A2 设备分支(@2c45117,commit 59a7906 MR!405):[`adaptor_patches/hunyuan.py#L580-L607`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-image-3.0/adaptor_patches/hunyuan.py)(`torch.npu.get_device_name()` / `"Ascend910B"` 设备分支:A2 走 `dist.reduce_scatter`、非 A2 走 `npu_mm_reduce_scatter_base`)+ [`docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/hunyuan-image-3.0/hunyuan_image_3_optimization.md)(MMReduceScatter 句尾「(A2平台下暂未使能该融合算子)」) |
| @@ -193,4 +69,4 @@ with torch.npu.amp.autocast(enabled=False): | |||
| 193 | ## Notes | 69 | ## Notes |
| 194 | 70 | ||
| 195 | - **gap**:源文档未给出 benchmark 数字、未列出 `attn_tp_size` / `moe_tp_size` / `ep_size` 推荐值 | 71 | - **gap**:源文档未给出 benchmark 数字、未列出 `attn_tp_size` / `moe_tp_size` / `ep_size` 推荐值 |
| 196 | -- **gap**:文档提到 `HunyuanMoE` 类与 `HunyuanImage3ForCausalMM` 类,但未解释 `HunyuanImage-3.0` 的文本 tower 细节(是否为独立 LLM) | 72 | +- **gap**:文档提到 `HunyuanMoE` 类与 `HunyuanImage3ForCausalMM` 类,但未解释 `HunyuanImage-3.0` 的文本 tower 细节(是否为独立 LLM) |
| @@ -1,7 +1,7 @@ | |||
| 1 | --- | 1 | --- |
| 2 | type: Concept | 2 | type: Concept |
| 3 | title: HunyuanVideo | 3 | title: HunyuanVideo |
| 4 | -description: HunyuanVideo 是腾讯发布的开源 DiT 文生视频模型;cann-recipes-infer 在 Atlas A2/A3 上实现了 NPU 适配与完整的推理性能优化方案。其 5s 720p(129 帧)生成任务里 **Attention 占 DiT 端到端 81%、DiT 占端到端 95%**,所以优化围绕长序列 Attention(FIA 融合算子、Ulysses / Ring SP、Ulysses Anything Attention、FP8 量化 FA、TopK / SVG 块稀疏)、DiT 多步缓存(FBCache / TeaCache / TaylorSeer)以及 VAE 侧(空间并行、mask 缓存、虚拟内存取消 offload)展开。叠加最优组合(ulysses8 + 融合算子 + VAE 并行 + VAE 优化 + 虚拟内存 + UAA)可在 Atlas A3 达到 **e2e 7.81×**(3768.91 s → 482.65 s);量化叠加(MXFP8 A8W8 + FP8 Attn + ulysses8 + VAE 并行)在 950PR 上 **e2e 1.38×**。 | 4 | +description: HunyuanVideo 的视频 DiT、文本编码与 VAE 结构;并行、缓存、量化和性能按版本分开维护。 |
| 5 | tags: | 5 | tags: |
| 6 | - task.design | 6 | - task.design |
| 7 | - topic.model_architecture | 7 | - topic.model_architecture |
| @@ -14,23 +14,27 @@ sources: | |||
| 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-video/hunyuan_video_optimization.md | 14 | - resource: https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-video/hunyuan_video_optimization.md |
| 15 | role: supporting | 15 | role: supporting |
| 16 | created_at: '2026-04-14T00:00:00Z' | 16 | created_at: '2026-04-14T00:00:00Z' |
| 17 | -updated_at: '2026-08-25T06:41:27Z' | 17 | +updated_at: '2026-10-22T10:36:46Z' |
| 18 | platforms: | 18 | platforms: |
| 19 | - a2 | 19 | - a2 |
| 20 | - a3 | 20 | - a3 |
| 21 | - '950' | 21 | - '950' |
| 22 | --- | 22 | --- |
| 23 | 23 | ||
| 24 | + | ||
| 25 | + | ||
| 24 | # HunyuanVideo | 26 | # HunyuanVideo |
| 25 | 27 | ||
| 26 | ## Summary | 28 | ## Summary |
| 27 | 29 | ||
| 28 | -HunyuanVideo 是腾讯发布的开源 DiT 文生视频模型;cann-recipes-infer 在 Atlas A2/A3 上实现了 NPU 适配与完整的推理性能优化方案。其 5s 720p(129 帧)生成任务里 **Attention 占 DiT 端到端 81%、DiT 占端到端 95%**,所以优化围绕长序列 Attention(FIA 融合算子、Ulysses / Ring SP、Ulysses Anything Attention、FP8 量化 FA、TopK / SVG 块稀疏)、DiT 多步缓存(FBCache / TeaCache / TaylorSeer)以及 VAE 侧(空间并行、mask 缓存、虚拟内存取消 offload)展开。叠加最优组合(ulysses8 + 融合算子 + VAE 并行 + VAE 优化 + 虚拟内存 + UAA)可在 Atlas A3 达到 **e2e 7.81×**(3768.91 s → 482.65 s);量化叠加(MXFP8 A8W8 + FP8 Attn + ulysses8 + VAE 并行)在 950PR 上 **e2e 1.38×**。 | 30 | +HunyuanVideo 是文生视频DiT模型,文本编码、双流/单流去噪Block和VAE解码构成不同阶段。Attention与视频长序列相关,量化、稀疏或跨步缓存是部署选择,不能当作模型恒定结构或质量保证。 |
| 31 | + | ||
| 32 | +平台配置、开关和验收步骤见[模型部署流程](../../guides/models/hunyuan_video_deployment.md),此处只保留模型机制。 | ||
| 29 | 33 | ||
| 30 | ## Architecture And Serving Traits | 34 | ## Architecture And Serving Traits |
| 31 | 35 | ||
| 32 | - **Backbone**:DiT(3D full attention),5s 720p ≈ 119k tokens 序列长度 | 36 | - **Backbone**:DiT(3D full attention),5s 720p ≈ 119k tokens 序列长度 |
| 33 | -- **双流/单流 Block**:Attention 按 `cu_seqlens` 拆分图像部分与文本部分分别计算 FA 再拼接(见下文 FIA 代码) | 37 | +- **双流/单流 Block**:Attention 按 `cu_seqlens` 分开有效图像+有效文本联合序列与剩余文本 padding,再拼接结果;首段长度是 img_len + text_mask.sum,不是图像/文本各自独立注意力 |
| 34 | - **Text Encoder**:LLM + CLIP(源文档未展开) | 38 | - **Text Encoder**:LLM + CLIP(源文档未展开) |
| 35 | - **目录结构**: | 39 | - **目录结构**: |
| 36 | - `models/hunyuan-video/sample_video.py` — 推理入口 | 40 | - `models/hunyuan-video/sample_video.py` — 推理入口 |
| @@ -43,193 +47,18 @@ HunyuanVideo 是腾讯发布的开源 DiT 文生视频模型;cann-recipes-infe | |||
| 43 | - `module/fa_quant/fa_quant.py` — FP8 量化 attention | 47 | - `module/fa_quant/fa_quant.py` — FP8 量化 attention |
| 44 | - `module/vae_patch_parallel.py` — VAE patch 并行 | 48 | - `module/vae_patch_parallel.py` — VAE patch 并行 |
| 45 | 49 | ||
| 46 | -### 统一推理拉起 | 50 | +## 部署与证据入口 |
| 47 | 51 | ||
| 48 | -部署经统一拉起架构 [mm-inference-launch](../frameworks/mm_inference_launch.md):`models/hunyuan-video/infer.sh` + YAML 场景切换(`mm_function.sh` 因 `model_name` 含 `hunyuan-video` 选用 `--cache-config` 连字符参数名)。config 变体(`config/`):`single`·`sp8`(基线,`dit_cache.method` 注释切 NoCache/FBCache/TeaCache/TaylorSeer)、`single_fp8`(FP8 FA + MXFP8 matmul,950PR)、`single_sparse`·`sp8_sparse`(块稀疏 Attention,默认 SVG 可切 TopK;`sp8_sparse` 为 Ulysses8)。 | 52 | +- [模型部署与验收流程](../../guides/models/hunyuan_video_deployment.md):配置、开关、失效条件与回退入口。 |
| 49 | - | 53 | +- [HunyuanVideo A3 累计优化的来源性能观测](../../optimizations/benchmark_observations/hunyuan_video_a3_source_benchmark.md):固定来源自报,未经本仓独立复测。 |
| 50 | -## 优化总览(源表) | 54 | +- [HunyuanVideo 950PR 量化的来源性能表](../../optimizations/benchmark_observations/hunyuan_video_950_source_benchmark.md):固定来源自报,未经本仓独立复测。 |
| 51 | - | 55 | +- [HunyuanVideo 稀疏与 Ring 的来源数据及平台缺口](../../optimizations/benchmark_observations/hunyuan_video_sparse_source_observations.md):来源条件待核验,保持draft。 |
| 52 | -| 优化方案 | 优化角度 | 方案介绍 | | ||
| 53 | -| ------- | -------- | -------- | | ||
| 54 | -| NPU 开箱,FA 融合算子适配 | 融合算子 | 使用 CANN 内部的 FIA 融合算子替代 transformer 中 attention 计算,算子内部通过 tiling 策略减少运行时的动态内存,并且加速 attention 计算。| | ||
| 55 | -| NPU RMS Norm 融合算子适配 | 融合算子 | 使用 CANN 内部的 RMSNorm 融合算子替代小算子实现。| | ||
| 56 | -| NPU ROPE 融合算子适配 | 融合算子 | 使用 CANN 内部的 `npu_rotary_mul` 融合算子替代小算子实现。| | ||
| 57 | -| Ulysses 序列并行 | 提高算力 | 将超长序列高效地切分到多个 NPU 上并行处理,每张卡负责处理自己负责的序列长度。Attention 以外切 S,Attention 处切 N。| | ||
| 58 | -| Ring Attention 序列并行 | 提高算力 | 与 Ulysses 类似,但 attention 计算时仍按 S 切分,通过 P2P 循环传递 KV 块。| | ||
| 59 | -| FBCache | 减少计算量 | 用第一个 DiT Block 的输出判断能否复用残差。| | ||
| 60 | -| TeaCache | 减少计算量 | 用第一个 img modulate 输出判断能否复用残差。| | ||
| 61 | -| Ulysses Anything Attention | 功能泛化 | 扩展 Ulysses 支持任意头数配置,动态填充 + 对齐。| | ||
| 62 | -| VAE 并行 | 提高算力 | 每张卡仅处理分配给自身的序列;为卡补充相邻卡部分序列解决卷积感受野。| | ||
| 63 | -| VAE 优化 | 减少计算量 | mask 缓存,减少重复构造。| | ||
| 64 | -| 取消 VAE offload | 减少搬运 | 开启虚拟内存,取消 CPU-offload。| | ||
| 65 | -| FP8 量化注意力 | 降低显存带宽 | per-block 动态量化 Q/K/V 为 FP8。| | ||
| 66 | -| MXFP8 矩阵量化 (A8W8) | 降低显存带宽 | DiT Linear 层 MXFP8,权重显存减半。| | ||
| 67 | -| 块稀疏 Attention | 减少计算量 | TopK / SVG 两种 predictor,基于 blitz_sparse_attention 算子。| | ||
| 68 | - | ||
| 69 | -## Known Optimizations | ||
| 70 | - | ||
| 71 | -### 融合算子 | ||
| 72 | - | ||
| 73 | -#### FIA 融合算子(`hyvideo/modules/attention.py:L119`) | ||
| 74 | - | ||
| 75 | -qkv 按序列长度拆分,图像部分与文本部分分别计算 FA 再拼接: | ||
| 76 | - | ||
| 77 | -```python | ||
| 78 | -elif mode == "flash": | ||
| 79 | - scale = 1.0 / math.sqrt(d) | ||
| 80 | - if cu_seqlens_q is None: | ||
| 81 | - x = torch_npu.npu_fused_infer_attention_score( | ||
| 82 | - q, k, v, | ||
| 83 | - num_heads=n, | ||
| 84 | - input_layout="BNSD", | ||
| 85 | - scale=scale, | ||
| 86 | - )[0] | ||
| 87 | - else: | ||
| 88 | - attn1 = torch_npu.npu_fused_infer_attention_score( | ||
| 89 | - q[:, :, :cu_seqlens_q[1], :], | ||
| 90 | - k[:, :, :cu_seqlens_kv[1], :], | ||
| 91 | - v[:, :, :cu_seqlens_kv[1], :], | ||
| 92 | - num_heads=n, input_layout="BNSD", scale=scale, | ||
| 93 | - )[0] | ||
| 94 | - attn2 = torch_npu.npu_fused_infer_attention_score( | ||
| 95 | - q[:, :, cu_seqlens_q[1]:, :], | ||
| 96 | - k[:, :, cu_seqlens_kv[1]:, :], | ||
| 97 | - v[:, :, cu_seqlens_kv[1]:, :], | ||
| 98 | - num_heads=n, input_layout="BNSD", scale=scale, | ||
| 99 | - )[0] | ||
| 100 | - x = torch.cat([attn1, attn2], dim=2) | ||
| 101 | -``` | ||
| 102 | - | ||
| 103 | -#### RMSNorm 融合算子(`hyvideo/modules/norm_layers.py:L55`) | ||
| 104 | - | ||
| 105 | -小算子实现 `x * rsqrt(mean(x^2) + eps)` 替换为: | ||
| 106 | - | ||
| 107 | -```python | ||
| 108 | -def forward(self, x): | ||
| 109 | - return torch_npu.npu_rms_norm(x, self.weight, epsilon=self.eps)[0] | ||
| 110 | -``` | ||
| 111 | - | ||
| 112 | -#### Rotary 融合算子(`hyvideo/modules/posemb_layers.py:L163`) | ||
| 113 | - | ||
| 114 | -```python | ||
| 115 | -xq_out = torch_npu.npu_rotary_mul(xq, cos, sin, rotary_mode="interleave") | ||
| 116 | -xk_out = torch_npu.npu_rotary_mul(xk, cos, sin, rotary_mode="interleave") | ||
| 117 | -``` | ||
| 118 | - | ||
| 119 | -### 序列并行 | ||
| 120 | - | ||
| 121 | -> 源:input shape `B, S, N, D`,序列并行沿 S 切分;attention 以外切 S,attention 处切 N(Ulysses)或继续切 S(Ring)。 | ||
| 122 | - | ||
| 123 | -- **Ulysses SP**:详见 [ulysses](../parallelism/ulysses.md) | ||
| 124 | -- **Ring Attention SP**:详见 [ring-attention](../parallelism/ring_attention.md) | ||
| 125 | -- **Ulysses Anything Attention (UAA)**:任意头数支持,详见 [ulysses](../parallelism/ulysses.md) | ||
| 126 | - | ||
| 127 | -### FP8 量化注意力 | ||
| 128 | - | ||
| 129 | -- Q 使用 `group_size=128`,K/V 使用 `group_size=256` | ||
| 130 | -- 详见 [fp8-attention](../quantization/fp8_attention.md) | ||
| 131 | -- **仅支持 Ulysses SP,不支持 Ring Attention** | ||
| 132 | - | ||
| 133 | -### MXFP8 矩阵量化 (A8W8) | ||
| 134 | - | ||
| 135 | -- per-token 激活 + per-group(32)权重,E4M3FN + E8M0 scale | ||
| 136 | -- 仅量化 `double_blocks` / `single_blocks` 中的 2D Linear,跳过 modulation 层与 1D 权重 | ||
| 137 | -- 详见 [mxfp8-a8w8](../quantization/mxfp8_a8w8.md) | ||
| 138 | - | ||
| 139 | -### DiT Cache(FBCache / TeaCache / TaylorSeer) | ||
| 140 | - | ||
| 141 | -- 详见 [dit-cache](../techniques/dit_cache.md) | ||
| 142 | -- Cache + SP 场景需 all_reduce 同步 `l1_loss` | ||
| 143 | - | ||
| 144 | -### VAE 并行 / 优化 / 取消 offload | ||
| 145 | - | ||
| 146 | -- VAE 并行:沿序列切 latent,每卡补充邻居序列避免卷积拼接缝 | ||
| 147 | -- mask 缓存:减少重复构造 mask 的计算开销 | ||
| 148 | -- 虚拟内存 + 取消 VAE offload:利用 `torch_npu` 虚拟内存扩展可用空间,避免反复 Host↔Device 搬运 | ||
| 149 | -- 详见 [vae-patch-parallel](../parallelism/vae_patch_parallel.md)、[vae](../modules/vae.md) | ||
| 150 | - | ||
| 151 | -### 块稀疏 Attention(TopK / SVG) | ||
| 152 | - | ||
| 153 | -- 基于 [blitz_sparse_attention](https://gitcode.com/cann/ops-transformer/blob/ca41b0b5eb9dbf6eb94a58aa20fe379681c1d751/experimental/attention/blitz_sparse_attention/README.md) 算子 | ||
| 154 | -- 详见 [blockwise-sparse-attention](../modules/blockwise_sparse_attention.md) | ||
| 155 | -- 配置入口:`models/hunyuan-video/hyvideo/sparse/sparse_config.yaml`;使能:`python sample_video.py --sparse_method TopK/SVG` | ||
| 156 | -- 模型专用 predictor 适配器:`HunyuanVideoTopKAdapter` / `HunyuanVideoSVGAdapter`(继承 `TopKPredictor` / `SVGPredictor`,承载双流/单流 DiT block 的稀疏接入,`sparse_block.py` 的 `sparse_double_block_forward` / `sparse_single_block_forward`) | ||
| 157 | - | ||
| 158 | -#### 块稀疏 × 序列并行协同 | ||
| 159 | - | ||
| 160 | -块稀疏当前只与单卡或 Ulysses 序列并行协同,**不与 Ring Attention 组合**。`sample_video.py` 在 `sparse_method != "no_sparse"` 时检测 `ring_degree > 1` 直接抛 `ValueError("Sparse attention does not support ring_degree > 1 in the current runtime.")`("current runtime" 限制)。 | ||
| 161 | - | ||
| 162 | -与 Ulysses 组合(`ulysses_degree > 1`)时,各 head 的稀疏 mask 不同、实际计算量不均,`sample_video.py` 经 `hasattr` 守卫**仅对实现了该方法的 TopK 路径**(`TopKPredictor` / `HunyuanVideoTopKAdapter`)调 `apply_head_reorder_for_load_balance(ulysses_degree)` 对 head 做负载均衡重排(按 head 计算量贪心装箱到各 Ulysses rank,详见 `sparse_method.py`);**SVG 路径未实现该方法,hasattr 守卫下跳过、不重排**。两路径均用 `parallel_sparse_attention`(`hyvideo/modules/attention.py:275`)作为 SP 下的执行入口。`config/sp8_sparse.yaml` 即 Ulysses8 + 块稀疏参考配置(默认 SVG,即走不重排路径)。 | ||
| 163 | - | ||
| 164 | -源文档 `Ulysses + TopK/SVG 联合优化` 性能(异构稀疏 vs 稠密 attention,DiT 耗时 s):单卡 3587→2160;四卡 904→576(负载均衡 564);八卡 464→335(负载均衡 321)。八卡相较稠密整体 ~1.39×–1.45×;四卡 head 已较均衡(负载均衡仅 ~12 s 增益),八卡并行度提升后 head 不均更突出(负载均衡 ~4.2%)。 | ||
| 165 | - | ||
| 166 | -## Benchmark | ||
| 167 | - | ||
| 168 | -### 性能优化(Atlas A3,baseline 仅做 NPU 开箱 FIA 替换) | ||
| 169 | - | ||
| 170 | -| 并行 | 融合算子 | ditcache | vae 并行 | vae 优化 | 虚拟内存<br/>取消 VAE offload | UAA | dit(s) | vae(s) | e2e | dit speedup | vae speedup | e2e speedup | | ||
| 171 | -| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | ||
| 172 | -| 1, **baseline** | x | x | x | x | x | x | 3634.48 | 133.15 | 3768.91 | 1.00 | 1.00 | 1.00 | | ||
| 173 | -| | √ | x | x | √ | √ | x | 3568.36 | 39.66 | 3612.30 | 1.02 | 3.36 | 1.04 | | ||
| 174 | -| | √ | FBCache | x | √ | x | x | 1693.89 | 83.03 | 1778.28 | 2.15 | 1.60 | 2.12 | | ||
| 175 | -| | √ | TeaCache | x | √ | x | x | 1737.92 | 88.56 | 1830.75 | 2.09 | 1.50 | 2.06 | | ||
| 176 | -| | √ | TaylorSeer | x | √ | x | x | 1405.68 | 92.85 | 1500.17 | 2.59 | 1.43 | **2.51** | | ||
| 177 | -| ulysses5 | √ | x | √ | √ | √ | √ | 769.33 | 40.61 | 811.38 | 4.72 | 3.28 | 4.65 | | ||
| 178 | -| ulysses8 | √ | x | √ | x | √ | x | 467.67 | 22.78 | 495.94 | 7.77 | 5.85 | 7.60 | | ||
| 179 | -| | √ | x | √ | √ | √ | x | 465.43 | 12.40 | 487.68 | 7.81 | 10.74 | 7.73 | | ||
| 180 | -| | √ | x | √ | √ | √ | √ | 477.12 | 13.10 | 482.65 | 7.62 | 10.16 | **7.81** | | ||
| 181 | -| | √ | x | √ | √ | √ | 730, 1300 | 511.12 | 13.06 | 525.75 | 7.11 | 10.20 | 7.17 | | ||
| 182 | - | ||
| 183 | -> Cache 配置:FBCache L1 阈值 0.05;TeaCache L1 阈值 0.15;TaylorSeer 算 1 跳 3,三阶泰勒。三种方法都能保持良好的推理精度(与 `cache_config.json` 配置一致)。 | ||
| 184 | - | ||
| 185 | -### 量化性能(950PR) | ||
| 186 | - | ||
| 187 | -| 并行 | 融合算子 | matmul 量化 | fa 量化 | vae 并行 | dit(s) | vae(s) | e2e | dit speedup | vae speedup | e2e speedup | | ||
| 188 | -| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | ||
| 189 | -| 1, **baseline** | √ | x | x | x | 2548.49 | 31.76 | 2580.87 | 1.00 | 1.00 | 1.00 | | ||
| 190 | -| | √ | √ | x | x | 2392.24 | 31.71 | 2424.52 | 1.07 | 1.00 | 1.06 | | ||
| 191 | -| | √ | x | √ | x | 1606.29 | 31.70 | 1638.55 | 1.59 | 1.00 | 1.58 | | ||
| 192 | -| | √ | √ | √ | x | 1539.24 | 31.43 | 1571.24 | 1.66 | 1.01 | 1.64 | | ||
| 193 | -| ulysses8, **baseline** | √ | x | x | √ | 246.29 | 10.27 | 256.92 | 1.00 | 1.00 | 1.00 | | ||
| 194 | -| | √ | √ | x | √ | 231.55 | 10.33 | 244.47 | 1.06 | 0.99 | 1.05 | | ||
| 195 | -| | √ | x | √ | √ | 181.85 | 10.50 | 193.96 | 1.35 | 0.98 | 1.32 | | ||
| 196 | -| | √ | √ | √ | √ | 170.04 | 10.17 | 185.54 | 1.45 | 1.01 | **1.38** | | ||
| 197 | - | ||
| 198 | -### 块稀疏 Attention 性能(TopK vs SVG,单卡) | ||
| 199 | - | ||
| 200 | -源文档「不同块稀疏 Attention 算法的性能数据」(`block_size_Q=128` / `block_size_K=512`;TopK 走离线 profiling 的 CAC 阈值 0.66、`sparse_time_step` 10-49;SVG `sparsity=0.8`、`sparse_time_step` 14-49): | ||
| 201 | - | ||
| 202 | -| Method | Sparsity | DiT Time (s) | E2E Time (s) | DiT Speedup | E2E Speedup | | ||
| 203 | -| ------ | -------- | -----------: | -----------: | ----------: | ----------: | | ||
| 204 | -| Baseline | – | 3459.52 | 3577.19 | 1.00× | 1.00× | | ||
| 205 | -| TopK | 76% | **1839.48** | **1963.52** | **1.88×** | **1.82×** | | ||
| 206 | -| SVG | 80% | 2036.18 | 2167.55 | 1.70× | 1.65× | | ||
| 207 | - | ||
| 208 | -> TopK 在更低稀疏度(76%)下 DiT 加速反而高于 SVG(80%):1.88× vs 1.70×;E2E 1.82× vs 1.65×。 | ||
| 209 | - | ||
| 210 | -### Ring Attention + TopK/SVG 性能(四卡) | ||
| 211 | - | ||
| 212 | -> **Ring 路径块稀疏机制**(与 Ulysses 路径不同):Ulysses 路径在 attention 前后复用 all-to-all,通信后每卡持有**完整序列 + 部分 head**,故可在完整序列语义上构造 `sabi/pattern` 并做 head 级负载均衡。**Ring 路径保留本卡 query 分片,并聚合全局 key/value**——Ring + TopK 用**本地 query 和全局 key/value** 构造 TopK `sabi`,并对首帧 query 用完整 key/value 做 dense attention 回填。**Ring + TopK + overlap** 进一步把 key/value 分成本地分片和其他分片:异步 `all_gather(k/v)` 的同时先算本地分片的稀疏 attention,通信完成后再算其他分片,并通过 **LSE 合并**两段输出。Ring + SVG 用本地 query 采样并同步 spatial/temporal MSE 中间量统一各卡 pattern,再按本地 query 全局时空坐标构造 `sabi`;当前 SVG overlap 配置复用普通 Ring + SVG 路径。配置:`config/sp8_sparse.yaml` 将 `ulysses-degree` 改 1、`ring-degree` 改 8 即走 Ring;`config/sp8_sparse_overlap.yaml` 为 Ring + TopK + overlap。 | ||
| 213 | - | ||
| 214 | -四卡 Ring Attention 下 DiT 推理耗时(s): | ||
| 215 | - | ||
| 216 | -| 方案 | `720*1280*209`, CAC `0.9` | `720*1280*209`, CAC `0.66` | `720*1280*129`, CAC `0.9` | `720*1280*129`, CAC `0.66` | | ||
| 217 | -| ---- | ------------------------: | -------------------------: | ------------------------: | -------------------------: | | ||
| 218 | -| Ring Attention | 2250 | 2250 | 949 | 949 | | ||
| 219 | -| Ring + TopK | 1877 | 1385 | 812 | 554 | | ||
| 220 | -| Ring + TopK + overlap | 1742 | 1327 | 822 | 584 | | ||
| 221 | - | ||
| 222 | -**overlap-payoff 分析**(源结论): | ||
| 223 | - | ||
| 224 | -1. `720*1280*209` 场景:Ring + TopK 把耗时从 `2250s` 降到 `1385s`(CAC 0.66);启用 overlap 进一步降到 `1327s`,相较稠密 Ring Attention 加速约 `1.70x`。 | ||
| 225 | -2. `720*1280*129` 场景:Ring + TopK 在 CAC `0.66` 时把耗时从 `949s` 降到 `554s`,相较稠密 Ring Attention 加速约 `1.71x`。 | ||
| 226 | -3. **overlap 收益与序列长度和 CAC 配置相关**:`720*1280*209`(长序列)收益更明显(1385→1327);`720*1280*129`(短序列)下,通信与计算重叠的收益不足以抵消额外分段计算开销(CAC 0.9 时 812→822、CAC 0.66 时 554→584,overlap 反而略慢)。 | ||
| 227 | 56 | ||
| 228 | ## Evidence | 57 | ## Evidence |
| 229 | 58 | ||
| 230 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-video/hunyuan_video_optimization.md)(582 行完整优化实践) | 59 | - [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/docs/models/hunyuan-video/hunyuan_video_optimization.md)(582 行完整优化实践) |
| 231 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/hunyuan-video/{README.md, sample_video.py, hyvideo/, scripts/}`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/hunyuan-video/{README.md, sample_video.py, hyvideo/, scripts/}) | 60 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`models/hunyuan-video/{README.md, sample_video.py, hyvideo/, scripts/}`](https://gitcode.com/cann/cann-recipes-infer/tree/2f82d10467e6fe8dae81c97a2cdac78d733a6703/models/hunyuan-video) |
| 232 | -- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`module/{unified_sp/, dit_cache/, blockwise_sparse/, fa_quant/, vae_patch_parallel.py}`](https://gitcode.com/cann/cann-recipes-infer/blob/2f82d10467e6fe8dae81c97a2cdac78d733a6703/module/{unified_sp/, dit_cache/, blockwise_sparse/, fa_quant/, vae_patch_parallel.py}) | 61 | +- [cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`module/{unified_sp/, dit_cache/, blockwise_sparse/, fa_quant/, vae_patch_parallel.py}`](https://gitcode.com/cann/cann-recipes-infer/tree/2f82d10467e6fe8dae81c97a2cdac78d733a6703/module) |
| 233 | - 块稀疏 × SP 协同(@2c45117,MR!353 / MR!369):[`models/hunyuan-video/sample_video.py#L116-L126`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-video/sample_video.py)(ring_degree ValueError + apply_head_reorder_for_load_balance 调用)、[`hyvideo/modules/attention.py#L275`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-video/hyvideo/modules/attention.py)(`parallel_sparse_attention`)、[`docs/models/hunyuan-video/hunyuan_video_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/hunyuan-video/hunyuan_video_optimization.md) `Ulysses + TopK/SVG 联合优化` 章节 | 62 | - 块稀疏 × SP 协同(@2c45117,MR!353 / MR!369):[`models/hunyuan-video/sample_video.py#L116-L126`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-video/sample_video.py)(ring_degree ValueError + apply_head_reorder_for_load_balance 调用)、[`hyvideo/modules/attention.py#L275`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/models/hunyuan-video/hyvideo/modules/attention.py)(`parallel_sparse_attention`)、[`docs/models/hunyuan-video/hunyuan_video_optimization.md`](https://gitcode.com/cann/cann-recipes-infer/blob/2c45117d781ab9ab88dcb89a28ae30f5d2450399/docs/models/hunyuan-video/hunyuan_video_optimization.md) `Ulysses + TopK/SVG 联合优化` 章节 |
| 234 | - 块稀疏 Attention 性能表(TopK 76% / SVG 80%):[cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md#不同块稀疏-attention算法的性能数据`](https://gitcode.com/cann/cann-recipes-infer/blob/46485bef9bb6bf061e4c356942bfea395e75865a/docs/models/hunyuan-video/hunyuan_video_optimization.md) | 63 | - 块稀疏 Attention 性能表(TopK 76% / SVG 80%):[cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md#不同块稀疏-attention算法的性能数据`](https://gitcode.com/cann/cann-recipes-infer/blob/46485bef9bb6bf061e4c356942bfea395e75865a/docs/models/hunyuan-video/hunyuan_video_optimization.md) |
| 235 | - Ring + TopK / Ring + TopK + overlap 性能表与 Ring 路径块稀疏机制:[cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md#ring-attention--topksvg-性能数据`](https://gitcode.com/cann/cann-recipes-infer/blob/46485bef9bb6bf061e4c356942bfea395e75865a/docs/models/hunyuan-video/hunyuan_video_optimization.md) `TopK/SVG + 序列并行联合优化` 章节 | 64 | - Ring + TopK / Ring + TopK + overlap 性能表与 Ring 路径块稀疏机制:[cann-recipes-infer](https://gitcode.com/cann/cann-recipes-infer/tree/2c45117d781ab9ab88dcb89a28ae30f5d2450399) — [`docs/models/hunyuan-video/hunyuan_video_optimization.md#ring-attention--topksvg-性能数据`](https://gitcode.com/cann/cann-recipes-infer/blob/46485bef9bb6bf061e4c356942bfea395e75865a/docs/models/hunyuan-video/hunyuan_video_optimization.md) `TopK/SVG + 序列并行联合优化` 章节 |
| @@ -254,6 +83,4 @@ xk_out = torch_npu.npu_rotary_mul(xk, cos, sin, rotary_mode="interleave") | |||
| 254 | 83 | ||
| 255 | ## Notes | 84 | ## Notes |
| 256 | 85 | ||
| 257 | -- **unverified**:块稀疏 Attention 的端到端 speedup 与画质对比未在 benchmark 表中给出 | 86 | +模块占比、累计加速与画质仅按独立观测的工作负载解释;不再假定所有表都是同一分辨率/帧数。HunyuanVideo此路径为dense DiT,不套用HunyuanImage的MoE路由。 |
| 258 | -- **gap**:源文档未给出 baseline 所用具体分辨率/帧数与 A3 硬件规格(只提"Atlas A3"),假定与正文描述 5s 720p 129 帧一致 | ||
| 259 | -- **gap**:与 HunyuanImage-3.0 的 MoE DoubleRouting 不同,HunyuanVideo 文档未涉及 MoE;HunyuanVideo DiT 纯 dense | ||
Rknowledge/model/inference/guides/experience/interventions/upstream_dit_video_monkeypatch_npu_adaptation.md→knowledge/model/inference/guides/bringup/upstream_dit_video_monkeypatch_npu_adaptation.md+19-13文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/quant_weight_conversion_path_selection.md→knowledge/model/inference/guides/deployment/quant_weight_conversion_path_selection.md+21-12
Rknowledge/model/inference/guides/experience/interventions/yuanrong_kv_pool_remote_h2d_ascend_prereqs.md→knowledge/model/inference/guides/deployment/yuanrong_kv_pool_remote_h2d_ascend_prereqs.md+12-10
Rknowledge/model/inference/guides/experience/interventions/blockwise_sparse_attention_ulysses_integration.md→knowledge/model/inference/guides/integration/blockwise_sparse_attention_ulysses_integration.md+9-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/graph_break_elimination_host_sync_unsupported_ops.md→knowledge/model/inference/guides/integration/graph_break_elimination_host_sync_unsupported_ops.md+8-4
Rknowledge/model/inference/guides/experience/interventions/sfa_dsa_cp_cross_group_kv_allgather.md→knowledge/model/inference/guides/integration/sfa_dsa_cp_cross_group_kv_allgather.md+43-12
Rknowledge/model/inference/optimizations/experience/rules/graph_mode_decode_only_compile_backends.md→knowledge/model/inference/guides/optimization/graph_mode_decode_only_compile_backends.md+108-26
Rknowledge/model/inference/guides/experience/interventions/prefill_micro_batch_dual_stream_moe_overlap.md→knowledge/model/inference/guides/optimization/prefill_micro_batch_dual_stream_moe_overlap.md+76-22
Rknowledge/model/inference/optimizations/experience/rules/staged_single_card_optimize_pipeline.md→knowledge/model/inference/guides/optimization/staged_single_card_optimize_pipeline.md+18-12文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/anti_patterns/ascend_310p_bringup_unusable_ops_and_nz_layout.md→knowledge/model/inference/optimizations/anti_patterns/ascend_310p_bringup_unusable_ops_and_nz_layout.md+4-4
Rknowledge/model/inference/optimizations/experience/anti_patterns/ascendc_op_input_validation_pitfalls.md→knowledge/model/inference/optimizations/anti_patterns/ascendc_op_input_validation_pitfalls.md+5-5
Rknowledge/model/inference/optimizations/experience/anti_patterns/ascendc_vector_kernel_silent_correctness_pitfalls.md→knowledge/model/inference/optimizations/anti_patterns/ascendc_vector_kernel_silent_correctness_pitfalls.md+3-3
Rknowledge/model/inference/optimizations/experience/anti_patterns/c8_kvcache_disables_pypto_lightning_indexer.md→knowledge/model/inference/optimizations/anti_patterns/c8_kvcache_disables_pypto_lightning_indexer.md+6-6
Rknowledge/model/inference/optimizations/experience/anti_patterns/index.md→knowledge/model/inference/optimizations/anti_patterns/index.md+9-7
Rknowledge/model/inference/optimizations/experience/anti_patterns/multinic_pd_advertise_address_launch_config.md→knowledge/model/inference/optimizations/anti_patterns/multinic_pd_advertise_address_launch_config.md+6-6
Rknowledge/model/inference/optimizations/experience/anti_patterns/npu_tensor_is_cuda_not_cuda_npu_discriminant.md→knowledge/model/inference/optimizations/anti_patterns/npu_tensor_is_cuda_not_cuda_npu_discriminant.md+3-3
Rknowledge/model/inference/optimizations/experience/anti_patterns/perf_measurement_distortion_input_and_metric.md→knowledge/model/inference/optimizations/anti_patterns/perf_measurement_distortion_input_and_metric.md+3-3
Rknowledge/model/inference/optimizations/experience/benchmark_observations/benchmark_deepseek_v32_quant_accuracy.md→knowledge/model/inference/optimizations/benchmark_observations/benchmark_deepseek_v32_quant_accuracy.md+29-14
Rknowledge/model/inference/optimizations/experience/cases/mla_prefill_flash_attention_ntd_layout.md→knowledge/model/inference/optimizations/cases/mla_prefill_flash_attention_ntd_layout.md+6-6
Rknowledge/model/inference/optimizations/experience/cases/mtp_benefit_sparse_attention_and_latency_metric.md→knowledge/model/inference/optimizations/cases/mtp_benefit_sparse_attention_and_latency_metric.md+20-18
Rknowledge/model/inference/guides/experience/interventions/qwen3_next_gdn_context_parallel_zeco.md→knowledge/model/inference/optimizations/cases/qwen3_next_gdn_context_parallel_zeco.md+37-13
Rknowledge/model/inference/optimizations/experience/rules/torchless_lowprec_dtype_acl_override.md→knowledge/model/inference/optimizations/cases/torchless_lowprec_dtype_acl_override.md+10-10
Rknowledge/model/inference/guides/experience/interventions/video_dit_taylorseer_cache_offload.md→knowledge/model/inference/optimizations/cases/video_dit_taylorseer_cache_offload.md+26-9
Rknowledge/model/inference/optimizations/experience/rules/950_mxfp8_moe_activation_scale_packed_layout.md→knowledge/model/inference/optimizations/rules/950_mxfp8_moe_activation_scale_packed_layout.md+3-3
Rknowledge/model/inference/guides/experience/interventions/aclgraph_decode_steady_state_skip_guard_reinplace.md→knowledge/model/inference/optimizations/rules/aclgraph_decode_steady_state_skip_guard_reinplace.md+4-4
Rknowledge/model/inference/optimizations/experience/cases/afd_attn_ffn_disaggregation.md→knowledge/model/inference/optimizations/rules/afd_attn_ffn_disaggregation.md+21-23
Rknowledge/model/inference/optimizations/experience/rules/ascend_deploy_parallel_variant_and_nondefault_platform.md→knowledge/model/inference/optimizations/rules/ascend_deploy_parallel_variant_and_nondefault_platform.md+8-8
Rknowledge/model/inference/optimizations/experience/rules/ascend_paged_attention_kv_cache.md→knowledge/model/inference/optimizations/rules/ascend_paged_attention_kv_cache.md+6-6
Rknowledge/model/inference/optimizations/experience/cases/ascendc_attention_op_tiling_budget.md→knowledge/model/inference/optimizations/rules/ascendc_attention_op_tiling_budget.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/ascendc_custom_op_out_of_band_dtype.md→knowledge/model/inference/optimizations/rules/ascendc_custom_op_out_of_band_dtype.md+6-6
Rknowledge/model/inference/optimizations/experience/rules/attention_compressor_tnd_layout_migration.md→knowledge/model/inference/optimizations/rules/attention_compressor_tnd_layout_migration.md+3-3文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/attention_to_fia_sparse_mode_sink_window.md→knowledge/model/inference/optimizations/rules/attention_to_fia_sparse_mode_sink_window.md+7-7
Rknowledge/model/inference/optimizations/experience/rules/bidirectional_attention_fia_non_causal_full.md→knowledge/model/inference/optimizations/rules/bidirectional_attention_fia_non_causal_full.md+4-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/c8_kv_scale_provenance_reuse_offline.md→knowledge/model/inference/optimizations/rules/c8_kv_scale_provenance_reuse_offline.md+12-13
Rknowledge/model/inference/optimizations/experience/rules/causal_cp_prefill_zigzag_actual_seqlens.md→knowledge/model/inference/optimizations/rules/causal_cp_prefill_zigzag_actual_seqlens.md+30-10文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/cfg_parallel_sample_level_diffusion.md→knowledge/model/inference/optimizations/rules/cfg_parallel_sample_level_diffusion.md+16-14文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/cuda_native_dit_video_attention_sp_npu_adaptation.md→knowledge/model/inference/optimizations/rules/cuda_native_dit_video_attention_sp_npu_adaptation.md+6-6文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/custom_op_framework_graph_backend_compat.md→knowledge/model/inference/optimizations/rules/custom_op_framework_graph_backend_compat.md+6-6
Rknowledge/model/inference/optimizations/experience/rules/decode_host_scalar_precompute_passthrough.md→knowledge/model/inference/optimizations/rules/decode_host_scalar_precompute_passthrough.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/deepseekv3_mixed_precision_quant_config_pitfalls.md→knowledge/model/inference/optimizations/rules/deepseekv3_mixed_precision_quant_config_pitfalls.md+2-2
Rknowledge/model/inference/optimizations/experience/rules/dit_cache_with_sequence_parallel.md→knowledge/model/inference/optimizations/rules/dit_cache_with_sequence_parallel.md+7-7
Rknowledge/model/inference/optimizations/experience/rules/dit_cnn_operator_algebraic_rewrite_npu.md→knowledge/model/inference/optimizations/rules/dit_cnn_operator_algebraic_rewrite_npu.md+63-8
Rknowledge/model/inference/optimizations/experience/rules/dit_mxfp_lowbit_quant_conv_as_linear.md→knowledge/model/inference/optimizations/rules/dit_mxfp_lowbit_quant_conv_as_linear.md+9-17文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/dit_step_cache_cfg_branch_slotting.md→knowledge/model/inference/optimizations/rules/dit_step_cache_cfg_branch_slotting.md+3-3文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/dit_step_cache_fbcache_teacache.md→knowledge/model/inference/optimizations/rules/dit_step_cache_fbcache_teacache.md+4-4
Aknowledge/model/inference/optimizations/rules/dp_attention_tp_oproj_mlp_hybrid.md+29-0文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/cases/dsa_decode_memory_budget_indexer_cache.md→knowledge/model/inference/optimizations/rules/dsa_decode_memory_budget_indexer_cache.md+18-7
Rknowledge/model/inference/optimizations/experience/rules/dsa_quant_layer_selection.md→knowledge/model/inference/optimizations/rules/dsa_quant_layer_selection.md+7-7
Rknowledge/model/inference/optimizations/experience/rules/dsa_sparse_prefill_mla_scheme_by_seqlen.md→knowledge/model/inference/optimizations/rules/dsa_sparse_prefill_mla_scheme_by_seqlen.md+14-3
Rknowledge/model/inference/optimizations/experience/rules/fused_allgather_ep_moe_256expert_deepseek_v3r1.md→knowledge/model/inference/optimizations/rules/fused_allgather_ep_moe_256expert_deepseek_v3r1.md+5-5
Rknowledge/model/inference/optimizations/experience/rules/fused_op_hardware_conditional_fallback.md→knowledge/model/inference/optimizations/rules/fused_op_hardware_conditional_fallback.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/generative_recommender_vocab_parallel_embedding.md→knowledge/model/inference/optimizations/rules/generative_recommender_vocab_parallel_embedding.md+4-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/gmm_swigluquant_vector_core_parallel.md→knowledge/model/inference/optimizations/rules/gmm_swigluquant_vector_core_parallel.md+7-7
Rknowledge/model/inference/optimizations/experience/rules/graph_fusion_register_replacement_guardrails.md→knowledge/model/inference/optimizations/rules/graph_fusion_register_replacement_guardrails.md+6-6
Rknowledge/model/inference/optimizations/experience/rules/graph_mode_kvcache_inplace_mla_prolog_adaptation.md→knowledge/model/inference/optimizations/rules/graph_mode_kvcache_inplace_mla_prolog_adaptation.md+3-3文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/graph_mode_multi_stream_cross_stream_sync_api.md→knowledge/model/inference/optimizations/rules/graph_mode_multi_stream_cross_stream_sync_api.md+175-13文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/hccl_aiv_expansion_vs_prefill_micro_batch.md→knowledge/model/inference/optimizations/rules/hccl_aiv_expansion_vs_prefill_micro_batch.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/hccl_comm_group_construction_pitfalls.md→knowledge/model/inference/optimizations/rules/hccl_comm_group_construction_pitfalls.md+8-8
Rknowledge/model/inference/optimizations/experience/rules/hide_moe_attention_tp_ep_comm.md→knowledge/model/inference/optimizations/rules/hide_moe_attention_tp_ep_comm.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/hixl_hccs_ipc_2mib_alignment.md→knowledge/model/inference/optimizations/rules/hixl_hccs_ipc_2mib_alignment.md+3-3
Rknowledge/model/inference/optimizations/experience/rules/hybrid_kv_cache_context_parallel_block_size_alignment.md→knowledge/model/inference/optimizations/rules/hybrid_kv_cache_context_parallel_block_size_alignment.md+3-3
Rknowledge/model/inference/optimizations/experience/rules/index.md→knowledge/model/inference/optimizations/rules/index.md+123-92
Rknowledge/model/inference/optimizations/experience/rules/inductor_autofuse_partial_compile_scope.md→knowledge/model/inference/optimizations/rules/inductor_autofuse_partial_compile_scope.md+11-19
Rknowledge/model/inference/optimizations/experience/rules/inference_preprocess_hotpath_host.md→knowledge/model/inference/optimizations/rules/inference_preprocess_hotpath_host.md+2-2
Rknowledge/model/inference/optimizations/experience/rules/kvcache_offload_batch_memcpy_async.md→knowledge/model/inference/optimizations/rules/kvcache_offload_batch_memcpy_async.md+3-3
Rknowledge/model/inference/guides/experience/interventions/kvcache_offload_host_swap.md→knowledge/model/inference/optimizations/rules/kvcache_offload_host_swap.md+13-9文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/kvcache_offload_sparse_selection_cross_step_reuse.md→knowledge/model/inference/optimizations/rules/kvcache_offload_sparse_selection_cross_step_reuse.md+6-6文件内容审核中,请稍后刷新重试
Aknowledge/model/inference/optimizations/rules/local_tp_memory_heavy_projections.md+86-0文件内容审核中,请稍后刷新重试
Aknowledge/model/inference/optimizations/rules/long_seq_prefill_context_parallel.md+73-0文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/mc2_dedicated_comm_domain.md→knowledge/model/inference/optimizations/rules/mc2_dedicated_comm_domain.md+4-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/mla_c8_int8_kv_cache_op_wiring.md→knowledge/model/inference/optimizations/rules/mla_c8_int8_kv_cache_op_wiring.md+10-8文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/mla_prolog_v3_prefill_seqlen_doc.md→knowledge/model/inference/optimizations/rules/mla_prolog_v3_prefill_seqlen_doc.md+12-3
Rknowledge/model/inference/optimizations/experience/rules/mla_prolog_zigzag_cp_kv_write.md→knowledge/model/inference/optimizations/rules/mla_prolog_zigzag_cp_kv_write.md+19-5
Rknowledge/model/inference/optimizations/experience/rules/mla_weight_absorption_latent_kv.md→knowledge/model/inference/optimizations/rules/mla_weight_absorption_latent_kv.md+30-8
Rknowledge/model/inference/optimizations/experience/rules/mlapo_fused_preprocess_w8a8_non_cp_gate.md→knowledge/model/inference/optimizations/rules/mlapo_fused_preprocess_w8a8_non_cp_gate.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/moe_dispatch_combine_comm_quant.md→knowledge/model/inference/optimizations/rules/moe_dispatch_combine_comm_quant.md+2-2
Rknowledge/model/inference/optimizations/experience/rules/moe_ep_decode_graphmode_dispatch.md→knowledge/model/inference/optimizations/rules/moe_ep_decode_graphmode_dispatch.md+5-4
Rknowledge/model/inference/optimizations/experience/rules/moe_ep_dispatch_path_by_stage.md→knowledge/model/inference/optimizations/rules/moe_ep_dispatch_path_by_stage.md+7-7文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/moe_ep_double_routing_skeleton.md→knowledge/model/inference/optimizations/rules/moe_ep_double_routing_skeleton.md+2-2文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/moe_expert_gmm_weight_lowbit_prep.md→knowledge/model/inference/optimizations/rules/moe_expert_gmm_weight_lowbit_prep.md+3-3文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/moe_role_based_precision_allocation.md→knowledge/model/inference/optimizations/rules/moe_role_based_precision_allocation.md+2-2
Rknowledge/model/inference/optimizations/experience/rules/moe_zero_expert_copy_expert_num.md→knowledge/model/inference/optimizations/rules/moe_zero_expert_copy_expert_num.md+8-8
Rknowledge/model/inference/optimizations/experience/rules/mrope_triton_ascend_vs_npu_mrope_decode.md→knowledge/model/inference/optimizations/rules/mrope_triton_ascend_vs_npu_mrope_decode.md+6-6
Rknowledge/model/inference/optimizations/experience/rules/mtp_cache_and_microbatch_prefill_bookkeeping.md→knowledge/model/inference/optimizations/rules/mtp_cache_and_microbatch_prefill_bookkeeping.md+8-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/mtp_decode_timing_two_metric_split.md→knowledge/model/inference/optimizations/rules/mtp_decode_timing_two_metric_split.md+33-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/mtp_draft_cache_state_correctness.md→knowledge/model/inference/optimizations/rules/mtp_draft_cache_state_correctness.md+22-14文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/multi_instance_online_master_addr.md→knowledge/model/inference/optimizations/rules/multi_instance_online_master_addr.md+15-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/multi_lora_bgmv_ascendc_vector_kernel.md→knowledge/model/inference/optimizations/rules/multi_lora_bgmv_ascendc_vector_kernel.md+58-29
Rknowledge/model/inference/guides/experience/interventions/multimachine_dp_ep_moe_collective_alignment.md→knowledge/model/inference/optimizations/rules/multimachine_dp_ep_moe_collective_alignment.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/cases/online_decode_kv_admission_retraction.md→knowledge/model/inference/optimizations/rules/online_decode_kv_admission_retraction.md+18-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/online_gloo_cpu_comm_domain_env_bringup.md→knowledge/model/inference/optimizations/rules/online_gloo_cpu_comm_domain_env_bringup.md+9-9
Rknowledge/model/inference/optimizations/experience/rules/op_backend_selection_ascendc_pypto_tilelang.md→knowledge/model/inference/optimizations/rules/op_backend_selection_ascendc_pypto_tilelang.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/paged_kv_cache_write_scatter_update_vs_scatter_nd.md→knowledge/model/inference/optimizations/rules/paged_kv_cache_write_scatter_update_vs_scatter_nd.md+7-7文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/pd_kv_transfer_timeout_from_hccl_rdma_budget.md→knowledge/model/inference/optimizations/rules/pd_kv_transfer_timeout_from_hccl_rdma_budget.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/pd_layerwise_kv_push_event_sync.md→knowledge/model/inference/optimizations/rules/pd_layerwise_kv_push_event_sync.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/perfect_eplb_profiling_upper_bound.md→knowledge/model/inference/optimizations/rules/perfect_eplb_profiling_upper_bound.md+6-6文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/precision_dit_fusion_autocast_cossin_dtype.md→knowledge/model/inference/optimizations/rules/precision_dit_fusion_autocast_cossin_dtype.md+4-4文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/precision_lightning_indexer_low_bit_quant.md→knowledge/model/inference/optimizations/rules/precision_lightning_indexer_low_bit_quant.md+3-3文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/precision_low_bit_quant_clip_clamp_outlier.md→knowledge/model/inference/optimizations/rules/precision_low_bit_quant_clip_clamp_outlier.md+3-3
Rknowledge/model/inference/optimizations/experience/rules/precision_quant_convert_model_config_pitfalls.md→knowledge/model/inference/optimizations/rules/precision_quant_convert_model_config_pitfalls.md+7-7文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/prefill_mla_dp_vs_sp_tp_sp_parallel.md→knowledge/model/inference/optimizations/rules/prefill_mla_dp_vs_sp_tp_sp_parallel.md+7-7文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/pypto_dsa_fused_attention_decompose_quant.md→knowledge/model/inference/optimizations/rules/pypto_dsa_fused_attention_decompose_quant.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/quant_lightning_indexer_latency.md→knowledge/model/inference/optimizations/rules/quant_lightning_indexer_latency.md+8-8
Rknowledge/model/inference/optimizations/experience/rules/quant_weight_load_layout_and_naming.md→knowledge/model/inference/optimizations/rules/quant_weight_load_layout_and_naming.md+7-7文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/guides/experience/interventions/ring_attention_comm_compute_overlap_lse_merge.md→knowledge/model/inference/optimizations/rules/ring_attention_comm_compute_overlap_lse_merge.md+6-6文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/spec_decode_entropy_verify_relax_rejection_threshold.md→knowledge/model/inference/optimizations/rules/spec_decode_entropy_verify_relax_rejection_threshold.md+6-6文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/structural_isomorphic_model_reuse_stack.md→knowledge/model/inference/optimizations/rules/structural_isomorphic_model_reuse_stack.md+8-8
Rknowledge/model/inference/optimizations/experience/rules/superkernel_ascendc_relocatable_binary.md→knowledge/model/inference/optimizations/rules/superkernel_ascendc_relocatable_binary.md+5-5文件内容审核中,请稍后刷新重试
Rknowledge/model/inference/optimizations/experience/rules/taylorseer_predictive_dit_cache.md→knowledge/model/inference/optimizations/rules/taylorseer_predictive_dit_cache.md+20-11
Rknowledge/model/inference/optimizations/experience/rules/tensor_prep_before_hccl_collective.md→knowledge/model/inference/optimizations/rules/tensor_prep_before_hccl_collective.md+3-3
Rknowledge/model/inference/optimizations/experience/rules/tnd_packed_sequence_standard_and_bsnd_shim.md→knowledge/model/inference/optimizations/rules/tnd_packed_sequence_standard_and_bsnd_shim.md+4-4
Rknowledge/model/inference/optimizations/experience/rules/torchair_cache_compile_persisted_graph.md→knowledge/model/inference/optimizations/rules/torchair_cache_compile_persisted_graph.md+4-4
Rknowledge/model/inference/guides/experience/interventions/vae_multicard_spatial_parallel.md→knowledge/model/inference/optimizations/rules/vae_multicard_spatial_parallel.md+10-9
Rknowledge/model/inference/optimizations/experience/rules/video_dit_blockwise_sparse_attention.md→knowledge/model/inference/optimizations/rules/video_dit_blockwise_sparse_attention.md+3-3
Rknowledge/model/inference/guides/experience/interventions/video_dit_fp8_mxfp8_quantization.md→knowledge/model/inference/optimizations/rules/video_dit_fp8_mxfp8_quantization.md+34-18
Rknowledge/model/inference/optimizations/experience/rules/video_dit_ulysses_sequence_parallel.md→knowledge/model/inference/optimizations/rules/video_dit_ulysses_sequence_parallel.md+5-5
Rknowledge/model/inference/optimizations/experience/rules/vision_encoder_rel_pos_bias_via_pse_shift.md→knowledge/model/inference/optimizations/rules/vision_encoder_rel_pos_bias_via_pse_shift.md+5-5
Rknowledge/model/inference/optimizations/experience/rules/w4a8_moe_gmm_prefill_skip_bias.md→knowledge/model/inference/optimizations/rules/w4a8_moe_gmm_prefill_skip_bias.md+21-7
Rknowledge/model/inference/optimizations/experience/rules/weight_nz_mc2_mm_all_reduce_exclusion.md→knowledge/model/inference/optimizations/rules/weight_nz_mc2_mm_all_reduce_exclusion.md+4-4
Rknowledge/model/inference/guides/experience/interventions/aclgraph_print_bisect_silent_hang.md→knowledge/model/inference/runbooks/runtime/aclgraph_print_bisect_silent_hang.md+7-7
Rknowledge/model/inference/optimizations/experience/rules/ascend_deployment_image_version_alignment.md→knowledge/model/inference/runbooks/runtime/ascend_deployment_image_version_alignment.md+18-10
Rknowledge/model/inference/optimizations/experience/rules/sfaa_nonpa_qkv_layout_constraint.md→knowledge/model/inference/runbooks/runtime/sfaa_nonpa_qkv_layout_constraint.md+14-10