diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cea5c9..58e6ab3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,9 +6,18 @@ on: pull_request: jobs: - # wt 2026-10-07-fix 新增 packaging 门: 算子包化回归(CPU 可跑部分) - # ——全算子 __init__.py 导入扫描; NPU 消费段在本机跑, CI 里跳过。 - # # wt + ops-catalog: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: 安装 CPU 依赖 + run: pip install pyyaml -q + - name: 算子目录一致性检查 + run: python3 scripts/build_ops_index.py --check + ops-packaging: runs-on: ubuntu-latest steps: @@ -20,7 +29,7 @@ jobs: run: | pip install pyyaml -q pip install torch --index-url https://download.pytorch.org/whl/cpu -q - - name: 算子包化导入扫描(16 目录) + - name: 算子包化导入扫描 run: | python3 - <<'EOF' import importlib, pathlib, sys @@ -36,7 +45,8 @@ jobs: except Exception as e: bad.append(f'{d.name}: {type(e).__name__}: {e}') if bad: - print('\n'.join(bad)); sys.exit(1) + print('\n'.join(bad)) + sys.exit(1) print(f'ops-packaging: {len(dirs)}/{len(dirs)} 目录导入+导出 OK') EOF @@ -51,8 +61,8 @@ jobs: run: | pip install pytest pyyaml -q pip install torch --index-url https://download.pytorch.org/whl/cpu -q - - name: 单元测试(无 GPU) - run: python -m pytest tests/unit -q + - name: 单元测试 + run: python3 -m pytest tests/unit -q docs-and-syntax: runs-on: ubuntu-latest @@ -67,12 +77,116 @@ jobs: errs = [] anchor_cache = {} - def anchors_of(p): - k = str(p) - if k not in anchor_cache: - text = p.read_text(encoding='utf-8', errors='replace') - hs = re.findall(r'^#{1,6}\s+(.*)$', text, re.M) - anchor_cache[k] = { - re.sub(r'[^\w\u4e00-\u9fff-]', '', h.strip().lower().replace(' ', '-')) - for h in hs} - return anchor_cache[k] + def anchors_of(path): + key = str(path) + if key not in anchor_cache: + try: + text = path.read_text(encoding='utf-8', errors='replace') + anchor_cache[key] = set(re.findall( + r' {target}') + elif anchor and anchor not in anchors_of(target_path): + errs.append(f'{md}: #{anchor} in {target}') + + print(f'检查完成: {len(errs)} 个错误') + for error in errs: + print(f' - {error}') + sys.exit(1 if errs else 0) + EOF + + - name: Python 语法检查 + run: | + python3 - <<'EOF' + import ast, pathlib, sys + fails = [] + for path in pathlib.Path('.').rglob('*.py'): + if '.git' in str(path): + continue + try: + ast.parse(path.read_text(), filename=str(path)) + except SyntaxError as exc: + fails.append(f'{path}: {exc}') + if fails: + for failure in fails: + print(f' - {failure}') + sys.exit(1) + print('Python 语法检查通过') + EOF + + - name: YAML 配置检查 + run: | + pip install pyyaml -q + python3 - <<'EOF' + import glob, sys, yaml + fails = [] + files = ( + glob.glob('configs/devices/*.yaml') + + glob.glob('inputs/*.yaml') + + ['.github/workflows/ci.yml', 'ops/catalog.yaml'] + ) + for name in files: + try: + with open(name, encoding='utf-8') as handle: + yaml.safe_load(handle) + except Exception as exc: + fails.append(f'{name}: {exc}') + if fails: + for failure in fails: + print(f' - {failure}') + sys.exit(1) + print('YAML 检查通过') + EOF + + - name: Intake manifest 契约校验 + run: python3 scripts/intake_validate.py --validate-only + + - name: 性能基线结构检查 + run: | + python3 - <<'EOF' + import glob, json, sys + fails = [] + files = glob.glob('perf/baselines/*.json') + [ + 'intake/manifest.schema.json' + ] + for name in files: + try: + with open(name, encoding='utf-8') as handle: + data = json.load(handle) + except Exception as exc: + fails.append(f'{name}: {exc}') + continue + if name.startswith('perf/'): + required = ( + 'schema', 'device', 'created_at', 'updated_at', + 'git_commit', 'env_fingerprint', 'cases' + ) + for key in required: + if key not in data: + fails.append(f'{name}: 缺字段 {key}') + for case_id, case in data.get('cases', {}).items(): + if 'latency_ms' not in case.get('metrics', {}): + fails.append(f'{name}: {case_id} 缺 metrics.latency_ms') + if fails: + for failure in fails: + print(f' - {failure}') + sys.exit(1) + print('性能基线结构检查通过') + EOF diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a671bd..380835d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,19 @@ ## [Unreleased] ### Changed +- **ops/ 文档契约同步(2026-10-13)**: 13 个 MLU 算子的 + README / REPORT / development / test-report 补记 PR #25 后的 + `counter + platform` 注册契约、canonical `AutogradPrivateUse1` / + 兼容 `PrivateUse1`、包态优先导入与 clone 包态执行用例;修正 + `empty` 的自用/实验路线与 sdpa_math 黄金口径描述;`ops/README` + 重组为核心多平台算子与 MLU 基础 ATen 算子两组,基础算子再按 + 布局/随机/分配/标量用途分组 +- **可扩放算子目录(2026-10-14)**: 新增 `ops/catalog.yaml` 作为 + 算子 / 平台矩阵唯一事实来源,登记六个目标厂商平台家族与当前增量 + 交付状态;`scripts/build_ops_index.py` 生成 `ops/README.md` 并校验 + profile、目录、平台声明和多平台文档,配套 + `docs/operator-catalog.md` 与单元测试;新增 `ops-catalog` CI 门, + 并恢复完整的 docs-and-syntax 链接 / 语法 / YAML / 基线检查 - sdpa `PLATFORM.md` 补记多算子 perf registry 顺序加载时的顶层 `reference` 遮蔽约束;backend 导入回归测试同步覆盖 P800 与 MLU diff --git a/docs/index.md b/docs/index.md index 513c360..e989373 100644 --- a/docs/index.md +++ b/docs/index.md @@ -64,7 +64,8 @@ flowchart TB | 完整走一遍开发 | [全链路指南](fullstack-guide.md)(七步流水线 + 覆盖矩阵) | | 开发完成后交付/集成 | [集成指南](integration.md)(路径选择 + 分发开销实测) | | 确认"精度/性能够不够" | [验收标准](acceptance.md)(口径 + Must/Should 分级 + 重验 runbook) | -| 存放/查找已开发算子 | [ops/](../ops/README.md)(样板同构 + 索引表) | +| 存放/查找已开发算子 | [ops/](../ops/README.md)(catalog 生成的增量矩阵) | +| 维护算子 / 平台矩阵 | [算子目录维护指南](operator-catalog.md)(六平台增量口径) | | 理解每层验证 | [测试体系](testing.md) | | 追踪性能回归 | [性能回归追踪](performance-regression.md) | | 接入 AI 生成的算子 | [AI 生成算子接入](ai-intake.md) | diff --git a/docs/operator-catalog.md b/docs/operator-catalog.md new file mode 100644 index 0000000..f451972 --- /dev/null +++ b/docs/operator-catalog.md @@ -0,0 +1,151 @@ +# 算子目录维护指南 + +这份指南说明 FlagOS-OP 如何维护持续增长的算子 / 平台矩阵,并避免把 +`ops/README.md` 变成难以维护的手工宽表。 + +## 事实来源 + +事实来源: + +```text +ops/catalog.yaml +``` + +生成文件: + +```text +ops/README.md +``` + +修改 catalog 后重新生成: + +```bash +python3 scripts/build_ops_index.py +``` + +检查生成文件是否最新: + +```bash +python3 scripts/build_ops_index.py --check +``` + +不要手工编辑 `ops/README.md`。文件内包含 AUTO-GENERATED 标记;生成结果 +直接入库,是为了让 GitHub 页面保持可读。 + +## 目标范围 + +catalog 当前建模六个厂商平台家族: + +| Family key | 显示名 | 当前状态 | +|---|---|---| +| `ascend` | 华为昇腾 | partial | +| `cambricon` | 寒武纪 | partial | +| `kunlunxin` | 昆仑芯 | partial | +| `thead` | 平头哥 | planned | +| `enflame` | 燧原 GCU | planned;已知型号 GCU300 / GCU400 / GCU410 | +| `biren` | 壁仞 | planned | + +状态语义: + +- **partial**:至少一个算子在该平台家族上有已合并证据;不表示所有算子都支持该平台。 +- **planned**:只表示目标范围;没有算子条目、profile、dispatch key 或支持承诺。 + +平台矩阵会增量补齐。不要为了表格看起来完整而预填空平台单元格。 + +## 数据结构 + +### `platform_families` + +每个平台家族记录: + +```yaml +: + display_name: <厂商显示名> + status: partial | planned + current_profiles: [<已有 configs/devices profile>] + known_variants: [<可选硬件型号>] + roadmap_note: <简短非承诺说明> +``` + +校验规则: + +- 每个 `current_profiles` 项必须存在 `configs/devices/.yaml`。 +- `planned` 家族不能声明 current profile。 +- 家族只有在至少一个算子登记后才可变为 `partial`。 + +### `operator_groups` + +分组描述算子存在的读者原因,而不是提交历史。当前有两类: + +- **core**:多平台计算算子,例如 attention、embedding。 +- **foundation**:用于验证执行链路和集成链路的基础 ATen 语义。 + +生成页按 `rank` 输出分组。现有分类不能准确描述新算子时,应新增分组, +不要把未来算子硬塞进语义不清的组。 + +### `operators` + +每个算子记录: + +```yaml +name: <算子名> +directory: +group: <分组 key> +purpose: <面向读者的一句话作用> +key_result: <有证据支撑的简短结果> +platforms: + : + profiles: [<已交付 profile>] + implementation: <实现摘要> +``` + +foundation 算子还需要顶层 `route`,除非每个平台条目单独声明 `route`。 +算子里的 platform 条目表示已交付声明,必须由该算子的测试和报告支撑。 + +跨多个平台家族的算子必须维护: + +```text +ops//PLATFORM.md +ops//MERGE.md +``` + +### `registration_contracts` + +注册契约按平台族记录,避免把一个厂商或一个算子族的 dispatch key 复制到 +无关代码。未来新增契约时应记录 canonical key、兼容 key、可注册算子数、 +不可注册算子和实现约束。 + +## 新增算子 + +1. 从 `templates/operator/` 复制并完成算子目录。 +2. 通过 kernel、op、framework/application 三层测试。 +3. 补齐 golden 与性能证据。 +4. 在 `ops/catalog.yaml` 的 `operators` 中登记。 +5. 运行 `python3 scripts/build_ops_index.py`。 +6. 确认 `python3 scripts/build_ops_index.py --check` 通过。 + +校验器要求 `ops/` 下每个目录都出现在 catalog 中,防止新算子合入后没有 +库存索引记录。 + +## 新增平台实现 + +1. 新增 `configs/devices/.yaml`。 +2. 把 profile 加入厂商家族的 `current_profiles`。 +3. 只有当首个算子的实现和验证合入后,才把家族从 `planned` 改为 `partial`。 +4. 在对应算子的 `platforms` 中登记该家族。 +5. 更新算子的 `PLATFORM.md`;第二个及后续平台还需维护 `MERGE.md`。 +6. 重新生成 `ops/README.md`。 + +新增厂商家族时,先登记 `planned` 家族和不承诺支持的 roadmap 说明;算子 +条目等到实现和验证落地后再添加。 + +## 文档边界 + +生成页只回答: + +- 当前有哪些算子; +- 哪些平台家族已有交付证据; +- 详细信息在哪里。 + +它不替代算子内文档。详细契约、fallback 边界、数值容差、性能协议和已知 +限制仍维护在各算子的 README、REPORT、PLATFORM 和报告分册中。 diff --git a/ops/README.md b/ops/README.md index 38281b7..249ab1d 100644 --- a/ops/README.md +++ b/ops/README.md @@ -1,8 +1,15 @@ # 已开发算子(ops/) -这个目录存放**按样板开发完成并交付的算子**——每个子目录一个算子, -结构与 [templates/operator](../templates/operator/README.md) 完全一致 -(kernel 三级 · test 三层 · goldendata · script · REPORT + 交付报告四件套)。 + + +这个目录存放按样板交付的算子。[ops/catalog.yaml](catalog.yaml) 是算子清单和平台 +覆盖的唯一事实来源;本页由脚本生成,避免手工维护长表格时漏改,也 +避免把局部平台结论误写成全局结论。 +维护方法和数据模型见[算子目录维护指南](../docs/operator-catalog.md)。 ## 新算子放进来 @@ -12,66 +19,129 @@ cd ops/<你的算子名>/ grep -rl my_op . | xargs sed -i 's/my_op/<你的算子名>/g' ``` -开发流程照[全链路指南](../docs/fullstack-guide.md)七步走;完成后: - -1. `python3 example.py ` 一键三层全绿 -2. 补齐交付报告四件套(REPORT + development + test-report + 分册, - 范本见 [softmax-fullstack](../examples/softmax-fullstack/reports/development.md)) -3. 在下方索引表登记一行 -4. 交付路径选择见[集成指南](../docs/integration.md)(FlagGems 贡献 / - aten 注册 / vllm_fl 插件 / 厂商身份) - -## 算子索引 - -> 硬件平台合计 **3 个**:`ascend910`(sdpa / sdpa_math / embedding)· `p800-kunlunxin`(sdpa / sdpa_math / embedding)· `cambricon`/`mlu590`(sdpa / embedding + 13 个 Torch/Triton 级算子)。 算子级明细见下表「硬件平台」列与各算子的 `REPORT.md` 表头。 - -| 算子 | 硬件平台 | 开发级别 | 路线 | 状态 | 交付报告 | 备注 | -|---|---|---|---|---|---|---| -| [sdpa](sdpa/)(scaled_dot_product_attention) | **3** · ascend910 / p800-kunlunxin / mlu590 | Triton / 厂商委托 | A1(aten 拦截) | ✅ 定稿 | [REPORT](sdpa/REPORT.md) | ascend910 + p800-kunlunxin + mlu590 · 三平台黄金 397/397(Ascend 历史 265/265) · [平台绑定](sdpa/PLATFORM.md) / [多平台合并](sdpa/MERGE.md) | -| [sdpa_math](sdpa_math/)(aten::_scaled_dot_product_attention_math) | 2 · ascend910 / p800-kunlunxin | Triton / vendor O/LSE + exact-P | A1(aten 拦截) | ✅ 定稿 | [REPORT](sdpa_math/REPORT.md) | SDPA math 后端(双输出 out+概率图)· 三平台黄金 175/175 · NPU fp16 1.16-2.03x · P800 direct 1.46-2.21x native | -| [embedding](embedding/)(aten::embedding) | **3** · ascend910 / p800-kunlunxin / cambricon | 厂商委托 / Triton 探针 | A1(aten 拦截) | ✅ 定稿 | [REPORT](embedding/REPORT.md) | 三平台稠密查表/反向 · Ascend 三层+350 PASS · MLU 174/174 逐位 · [平台绑定](embedding/PLATFORM.md) / [多平台合并](embedding/MERGE.md) | - -| [type_as](type_as/) | 1 · cambricon | Triton 级 | A1 aten | 三层全绿 | [REPORT.md](type_as/REPORT.md) | 黄金 111/111;位级一致 | -| [clone](clone/) | 1 · cambricon | Triton 级 | A1 aten | 三层全绿 | [REPORT.md](clone/REPORT.md) | 黄金 48/48;存储独立 | -| [contiguous](contiguous/) | 1 · cambricon | Triton 级 | A1 aten | 三层全绿 | [REPORT.md](contiguous/REPORT.md) | 黄金 114/114;转置 swiz 提速 ~80x | -| [copy_](copy_/) | 1 · cambricon | Triton 级 | A1 aten | 三层全绿 | [REPORT.md](copy_/REPORT.md) | 黄金 78/78;原地+广播 | -| [dropout](dropout/) | 1 · cambricon | Triton 级 | A1 aten | 三层全绿 | [REPORT.md](dropout/REPORT.md) | 黄金 189/189;随机分支统计判定 | -| [empty_like](empty_like/) | 1 · cambricon | torch 级 | A1 aten | 三层全绿 | [REPORT.md](empty_like/REPORT.md) | 分配/元数据;黄金 168/168(元数据) | -| [empty](empty/) | 1 · cambricon | torch 级 | 自用/实验 | 三层全绿 | [REPORT.md](empty/REPORT.md) | 分配原语,不注册(递归风险);元数据 48/48 | -| [empty_strided](empty_strided/) | 1 · cambricon | torch 级 | A1 aten | 三层全绿 | [REPORT.md](empty_strided/REPORT.md) | 分配/元数据;元数据 30/30 | -| [detach](detach/) | 1 · cambricon | torch 级 | A1 aten | 三层全绿 | [REPORT.md](detach/REPORT.md) | 别名词义;黄金 36/36 | -| [detach_](detach_/) | 1 · cambricon | torch 级 | 自用/实验 | 三层全绿 | [REPORT.md](detach_/REPORT.md) | 不可 A1 注册(autograd key);36/36 | -| [item](item/) | 1 · cambricon | torch 级 | A1 aten | 三层全绿 | [REPORT.md](item/REPORT.md) | host 标量;黄金 36/36 | -| [_local_scalar_dense](_local_scalar_dense/) | 1 · cambricon | torch 级 | A1 aten | 三层全绿 | [REPORT.md](_local_scalar_dense/REPORT.md) | host 标量原语;36/36 | -| [result_type](result_type/) | 1 · cambricon | torch 级 | 自用/实验 | 三层全绿 | [REPORT.md](result_type/REPORT.md) | dtype 元数据;黄金 8/8,矩阵 64 项 | - -> 均为 MLU(cambricon)实现。合计 13 个算子。 -> 应用层:各 `test/framework_level.py` 为快速等价验证;另有**真实 vLLM** 的 -> A1 多算子注入验证见 `tests/framework_level/test_a1_aten_ops.py` -> (sitecustomize 桥 + 基线/注入双跑,断言输出逐位一致)。 -> 注:`detach` 可单独注册,但在真实 vLLM 建模时会破坏 `nn.Parameter` -> 构造,故不纳入多算子注入集(与 `detach_` 同属进程内不可共存)。 - -> 结构同构的教学样例 [softmax-fullstack](../examples/softmax-fullstack/) -> 保留在 examples/(它的定位是"教",ops/ 的定位是"用"); -> 后续正式开发的算子一律进 ops/ 并在此登记。 - -## 约定 - -- **目录名 = 算子名**(小写下划线,如 `gelu_and_mul`) -- **交付自查清单**: 见 templates/operator/README「交付自查清单」节 - (2026-10-07 审计后新增——13 算子曾缺包化入口已补齐,后续算子 - 合并前照单自查,勿再欠账) -- **包化导入(多算子同进程必用)**: 目录含 `__init__.py`, - `from ops.sdpa import register_a1` / `from ops.embedding import - register_a1` / `from ops.sdpa_math import register_a1` 顶层名带包前缀, - 互不遮蔽(根因与验证见 - sdpa/reports/e2e_mini_llm.md + ops/test_packaging.py); - 单算子脚本模式(sys.path 注入目录)继续兼容 -- 未实现的级别**置空 + 说明**(`kernel/hardware_level/README.md`), - 不留空文件 -- 命名/级别/路线术语遵循 [体系结构](../docs/architecture.md); - Triton kernel 必须内置 [#11](../docs/known-issues.md) device 上下文与 - [#15](../docs/known-issues.md) 尾块防护两条硬约束 -- 合入本目录前: 三层测试全绿 + 黄金回归 + perf 门禁通过 - (见[集成指南交付 checklist](../docs/integration.md#checklist)) +开发流程照[全链路指南](../docs/fullstack-guide.md)七步走。完成后: + +1. 运行 `python3 example.py `,确认三层测试全绿。 +2. 补齐 REPORT 和 development / test-report / accuracy / performance 分册。 +3. 在 `ops/catalog.yaml` 登记算子、用途、平台和证据。 +4. 运行 `python3 scripts/build_ops_index.py` 更新本页。 +5. 交付路径选择见[集成指南](../docs/integration.md)。 + +## 平台覆盖口径 + +目标范围是 **6 个厂商平台家族**。当前登记 **16 个算子**:**3 个核心多平台算子**、**13 个基础 ATen 算子**。 +这是一个增量交付子集,不是完整平台矩阵。`planned` 只表示目标范围, +不表示支持完成;只有进入算子条目并带有测试与报告的平台才计为已交付。 + +| 目标平台 | 当前状态 | 已登记算子 | 当前 profile / 型号 | +|---|---|---:|---| +| 华为昇腾(`ascend`) | 部分交付 | 3 | `ascend910` | +| 寒武纪(`cambricon`) | 部分交付 | 15 | `cambricon` · `mlu590` | +| 昆仑芯(`kunlunxin`) | 部分交付 | 3 | `p800-kunlunxin` | +| 平头哥(`thead`) | 规划中 | 0 | 待首个交付算子确定 | +| 燧原 GCU(`enflame`) | 规划中 | 0 | 计划覆盖:GCU300 / GCU400 / GCU410 | +| 壁仞(`biren`) | 规划中 | 0 | 待首个交付算子确定 | + +## 核心多平台算子 + +覆盖注意力、私有 math 后端和 embedding,具备跨平台交付证据。 + +| 算子 | 作用 | 平台 | 关键结论 | +|---|---|---|---| +| [sdpa](sdpa/) | 标准 scaled-dot-product attention 前向路径。 | 华为昇腾(ascend910) · 昆仑芯(p800-kunlunxin) · 寒武纪(mlu590) | 三平台黄金覆盖 397/397。 | +| [sdpa_math](sdpa_math/) | 私有 SDPA math 后端,同时返回完整概率图。 | 华为昇腾(ascend910) · 昆仑芯(p800-kunlunxin) | 四份实现通过同一组 175 例黄金数据。 | +| [embedding](embedding/) | 稠密 embedding 前向和 dense backward。 | 华为昇腾(ascend910) · 昆仑芯(p800-kunlunxin) · 寒武纪(cambricon / mlu590) | 已在三个当前厂商平台交付;MLU 侧具备逐位覆盖。 | + +## 基础 ATen 算子 + +这些算子主要验证基础语义、执行链路、A1 注册和真实框架注入。 +它们不一定以性能提升为首要目标,因此按用途分组,避免和核心计算 +算子混在同一张长表里。 + +### 布局、复制与类型转换 + +覆盖张量布局、存储、广播和类型转换语义,用于验证执行链路和注册链路。 + +| 算子 | 作用 | 交付路线 | 关键结论 | +|---|---|---|---| +| [type_as](type_as/) | 把 tensor 转换到另一个 tensor 的 dtype,并留在原设备。 | A1 · Triton | 黄金 111/111,结果逐位一致。 | +| [clone](clone/) | 复制 tensor 到独立存储。 | A1 · Triton | 黄金 48/48;op 层测试覆盖包态执行。 | +| [contiguous](contiguous/) | 返回连续存储;输入本身连续时保持别名。 | A1 · Triton | 黄金 114/114;转置路径使用 2D swizzle 调度。 | +| [copy_](copy_/) | 写入已有 tensor,支持布局和广播。 | A1 · Triton | 黄金 78/78。 | + +### 随机与 autograd 语义 + +覆盖随机分支和 detach 语义,用于验证非纯逐元素行为和 autograd 层语义。 + +| 算子 | 作用 | 交付路线 | 关键结论 | +|---|---|---|---| +| [dropout](dropout/) | 验证随机 dropout 语义和统计行为。 | A1 · Triton | 黄金 189/189。 | +| [detach](detach/) | 返回共享存储的非原地 detach 视图。 | A1 · torch | 黄金 36/36。 | +| [detach_](detach_/) | 在基础算子族中保留原地 detach 语义。 | experimental · torch | 按设计不可 A1 注册,保持 NotImplementedError。 | + +### 分配与元数据 + +覆盖分配、stride、未初始化内存和 dtype 提升语义;这类算子以元数据判卷。 + +| 算子 | 作用 | 交付路线 | 关键结论 | +|---|---|---|---| +| [empty_like](empty_like/) | 按另一个 tensor 的元数据分配未初始化 tensor。 | A1 · torch | 元数据黄金 168/168。 | +| [empty](empty/) | 按 size 分配未初始化 tensor。 | experimental · torch | 不注册,避免与 empty_strided 递归;元数据黄金 48/48。 | +| [empty_strided](empty_strided/) | 按显式 size 和 stride 分配未初始化 tensor。 | A1 · torch | 元数据黄金 30/30。 | +| [result_type](result_type/) | 计算两个输入的 PyTorch dtype 提升结果。 | experimental · torch | 纯元数据函数;按设计不可 A1 注册。 | + +### Host 标量 + +覆盖单元素 tensor 转 Python 标量的路径,包括公开 item API 底层的私有 ATen 原语。 + +| 算子 | 作用 | 交付路线 | 关键结论 | +|---|---|---|---| +| [item](item/) | 把单元素 tensor 转换为 Python 标量。 | A1 · torch | 黄金 36/36。 | +| [_local_scalar_dense](_local_scalar_dense/) | 覆盖 item 底层的稠密标量私有原语。 | A1 · torch | 黄金 36/36。 | + +## 平台注册契约 + +注册契约按平台族记录,不把某个平台的 dispatch key 推广成全局规则。 + +### MLU 基础算子注册契约 + +适用范围:Cambricon MLU 上的基础 ATen 算子族。 + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter: dict | None = None, + platform=, +) +``` + +- Canonical key:`AutogradPrivateUse1` +- Compatibility key:`PrivateUse1` +- 可注册算子:10 +- 不可注册算子:`empty`、`detach_`、`result_type` +- generic Autograd 不作为单平台默认映射。 +- 可注册实现的导入顺序为包态优先、单算子脚本态 fallback。 +- clone 在 op 层测试中保留包态实际执行代表用例。 + +## 维护规则 + +- **目录名 = 算子名**,使用小写下划线。 +- **新增平台家族**先在 catalog 登记 display name、status 和 roadmap, + 再随首个算子交付 profile;不要提前把规划平台写进 operator 条目。 +- **新增算子**同步更新 catalog、三层测试、golden、性能数据和报告。 +- **多平台算子**必须维护 `PLATFORM.md`;涉及平台合并流程时维护 `MERGE.md`。 +- **平台结论限定范围**:dtype、shape、dispatch key、运行时版本和 + fallback 边界写在同一份证据链里。 +- **包态导入**统一使用 `from ops. import register_a1`;脚本态 + 仅作为单算子目录内运行的兼容模式。 +- CI 的 `ops-packaging` 是 CPU 包入口扫描;`ops/test_packaging.py` 是 + NPU 真机 E2E,两者口径不同。 +- 修改 catalog 后必须运行: + + ```bash + python3 scripts/build_ops_index.py + ``` + +- 合入前照[交付自查清单](../templates/operator/README.md)检查。 + + diff --git a/ops/_local_scalar_dense/README.md b/ops/_local_scalar_dense/README.md index 80bcfa3..1ba2b06 100644 --- a/ops/_local_scalar_dense/README.md +++ b/ops/_local_scalar_dense/README.md @@ -26,8 +26,25 @@ python3 script/bench_perf.py --device mlu ## 关键点 -1. 注册:aten::_local_scalar_dense@PrivateUse1(拦截 1 次)。 +1. 注册:aten::_local_scalar_dense@AutogradPrivateUse1(兼容 PrivateUse1;拦截 1 次)。 2. 判定:标量值相等 + Python 类型 + 多元素报错。 3. 无 Triton/硬件级(该类算子没有可编译的元素级设备码)。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops._local_scalar_dense import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/_local_scalar_dense/REPORT.md b/ops/_local_scalar_dense/REPORT.md index 7a1f48b..a830a44 100644 --- a/ops/_local_scalar_dense/REPORT.md +++ b/ops/_local_scalar_dense/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `_local_scalar_dense`(host 标量类) | | 语义 | [reference.py](reference.py):item 的底层原语:单元素稠密张量 → host 标量 | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/` | ✅ 36/36 | | 性能 | 采样 | ✅ 0.0965ms | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `_local_scalar_dense` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops._local_scalar_dense import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;标量值相等 + Python 类型 + 多元素报错。无 Triton/硬件级(该类算子固有)。 diff --git a/ops/_local_scalar_dense/reports/development.md b/ops/_local_scalar_dense/reports/development.md index 76800a3..1c3686e 100644 --- a/ops/_local_scalar_dense/reports/development.md +++ b/ops/_local_scalar_dense/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/_local_scalar_dense/reports/test-report.md b/ops/_local_scalar_dense/reports/test-report.md index 7a60da8..3072b14 100644 --- a/ops/_local_scalar_dense/reports/test-report.md +++ b/ops/_local_scalar_dense/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `_local_scalar_dense` / A1 / torch 级(host 标量) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py` | PASS(12 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | aten::_local_scalar_dense@PrivateUse1(拦截 1 次) | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金 | ✅ | `script/` | 36/36 | diff --git a/ops/catalog.yaml b/ops/catalog.yaml new file mode 100644 index 0000000..0b62673 --- /dev/null +++ b/ops/catalog.yaml @@ -0,0 +1,267 @@ +# ops/README.md 的唯一事实来源。 +# +# Rules: +# - 只登记已交付证据;不要把未来移植写成已完成。 +# - 规划中的厂商只放在 platform_families;测试和报告合入后再加入算子条目。 +# - 修改本文件后运行 scripts/build_ops_index.py。 + +schema: 1 +updated: 2026-10-14 + +target_scope: + vendor_family_count: 6 + interpretation: 目标范围是六个厂商平台家族;当前仓库是增量子集,不是完整矩阵。 + +platform_families: + ascend: + display_name: 华为昇腾 + status: partial + current_profiles: [ascend910] + roadmap_note: 当前集中在注意力和 embedding 算子。 + cambricon: + display_name: 寒武纪 + status: partial + current_profiles: [cambricon, mlu590] + roadmap_note: 覆盖注意力、embedding 和基础 ATen 算子族。 + kunlunxin: + display_name: 昆仑芯 + status: partial + current_profiles: [p800-kunlunxin] + roadmap_note: 覆盖当前 P800 XMLIR 栈。 + thead: + display_name: 平头哥 + status: planned + current_profiles: [] + roadmap_note: 首个算子交付时补充 profile 与命名。 + enflame: + display_name: 燧原 GCU + status: planned + current_profiles: [] + known_variants: [GCU300, GCU400, GCU410] + roadmap_note: 支持落地后记录型号差异。 + biren: + display_name: 壁仞 + status: planned + current_profiles: [] + roadmap_note: 首个算子交付时补充 profile 与命名。 + +operator_groups: + core: + title: 核心多平台算子 + kind: core + rank: 1 + description: >- + 覆盖注意力、私有 math 后端和 embedding,具备跨平台交付证据。 + layout-copy: + title: 布局、复制与类型转换 + kind: foundation + rank: 2 + description: 覆盖张量布局、存储、广播和类型转换语义,用于验证执行链路和注册链路。 + random-autograd: + title: 随机与 autograd 语义 + kind: foundation + rank: 3 + description: 覆盖随机分支和 detach 语义,用于验证非纯逐元素行为和 autograd 层语义。 + allocation-metadata: + title: 分配与元数据 + kind: foundation + rank: 4 + description: 覆盖分配、stride、未初始化内存和 dtype 提升语义;这类算子以元数据判卷。 + host-scalar: + title: Host 标量 + kind: foundation + rank: 5 + description: 覆盖单元素 tensor 转 Python 标量的路径,包括公开 item API 底层的私有 ATen 原语。 + +operators: + - name: sdpa + directory: sdpa + group: core + purpose: 标准 scaled-dot-product attention 前向路径。 + key_result: 三平台黄金覆盖 397/397。 + platforms: + ascend: + profiles: [ascend910] + implementation: Triton + kunlunxin: + profiles: [p800-kunlunxin] + implementation: 厂商 efficient attention 委托 + cambricon: + profiles: [mlu590] + implementation: TMO / fused overrideable 委托 + + - name: sdpa_math + directory: sdpa_math + group: core + purpose: 私有 SDPA math 后端,同时返回完整概率图。 + key_result: 四份实现通过同一组 175 例黄金数据。 + platforms: + ascend: + profiles: [ascend910] + implementation: Triton exact-P 路径 + kunlunxin: + profiles: [p800-kunlunxin] + implementation: vendor O/LSE 加 exact-P 重建 + + - name: embedding + directory: embedding + group: core + purpose: 稠密 embedding 前向和 dense backward。 + key_result: 已在三个当前厂商平台交付;MLU 侧具备逐位覆盖。 + platforms: + ascend: + profiles: [ascend910] + implementation: 厂商 index_select 与 dense_backward 委托 + kunlunxin: + profiles: [p800-kunlunxin] + implementation: 原生 row-gather 与反向补偿 + cambricon: + profiles: [cambricon, mlu590] + implementation: 原生 index_select 与 embedding_backward 委托 + + - name: type_as + directory: type_as + group: layout-copy + purpose: 把 tensor 转换到另一个 tensor 的 dtype,并留在原设备。 + route: A1 · Triton + key_result: 黄金 111/111,结果逐位一致。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: clone + directory: clone + group: layout-copy + purpose: 复制 tensor 到独立存储。 + route: A1 · Triton + key_result: 黄金 48/48;op 层测试覆盖包态执行。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: contiguous + directory: contiguous + group: layout-copy + purpose: 返回连续存储;输入本身连续时保持别名。 + route: A1 · Triton + key_result: 黄金 114/114;转置路径使用 2D swizzle 调度。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: copy_ + directory: copy_ + group: layout-copy + purpose: 写入已有 tensor,支持布局和广播。 + route: A1 · Triton + key_result: 黄金 78/78。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: dropout + directory: dropout + group: random-autograd + purpose: 验证随机 dropout 语义和统计行为。 + route: A1 · Triton + key_result: 黄金 189/189。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: detach + directory: detach + group: random-autograd + purpose: 返回共享存储的非原地 detach 视图。 + route: A1 · torch + key_result: 黄金 36/36。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: detach_ + directory: detach_ + group: random-autograd + purpose: 在基础算子族中保留原地 detach 语义。 + route: experimental · torch + key_result: 按设计不可 A1 注册,保持 NotImplementedError。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: empty_like + directory: empty_like + group: allocation-metadata + purpose: 按另一个 tensor 的元数据分配未初始化 tensor。 + route: A1 · torch + key_result: 元数据黄金 168/168。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: empty + directory: empty + group: allocation-metadata + purpose: 按 size 分配未初始化 tensor。 + route: experimental · torch + key_result: 不注册,避免与 empty_strided 递归;元数据黄金 48/48。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: empty_strided + directory: empty_strided + group: allocation-metadata + purpose: 按显式 size 和 stride 分配未初始化 tensor。 + route: A1 · torch + key_result: 元数据黄金 30/30。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: result_type + directory: result_type + group: allocation-metadata + purpose: 计算两个输入的 PyTorch dtype 提升结果。 + route: experimental · torch + key_result: 纯元数据函数;按设计不可 A1 注册。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: item + directory: item + group: host-scalar + purpose: 把单元素 tensor 转换为 Python 标量。 + route: A1 · torch + key_result: 黄金 36/36。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + + - name: _local_scalar_dense + directory: _local_scalar_dense + group: host-scalar + purpose: 覆盖 item 底层的稠密标量私有原语。 + route: A1 · torch + key_result: 黄金 36/36。 + platforms: + cambricon: + profiles: [cambricon, mlu590] + +registration_contracts: + cambricon-foundation: + title: MLU 基础算子注册契约 + scope: Cambricon MLU 上的基础 ATen 算子族。 + signature: + dispatch_key: AutogradPrivateUse1 + counter: dict | None + platform: 显式 platform 名称 + canonical_key: AutogradPrivateUse1 + compatibility_key: PrivateUse1 + registerable_count: 10 + non_registerable_operators: [empty, detach_, result_type] + notes: + - generic Autograd 不作为单平台默认映射。 + - 可注册实现的导入顺序为包态优先、单算子脚本态 fallback。 + - clone 在 op 层测试中保留包态实际执行代表用例。 diff --git a/ops/clone/README.md b/ops/clone/README.md index 1c6a5d6..e9a1065 100644 --- a/ops/clone/README.md +++ b/ops/clone/README.md @@ -7,7 +7,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`torch.Tensor.clone`) | -| 路线 | A1:`Library("aten","IMPL").impl("clone", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 设备 | `cambricon`(MLU590-M9) | | 语义 | 独立新张量,shape/dtype/device 同 self,不共享存储;preserve_format | @@ -31,4 +31,21 @@ python3 script/bench_perf.py --device mlu --impl triton 3. 参考实现用原生 `.clone`:op 层测试需在**注册前**取原生参考。 4. 未实现硬件级(无厂商 clone 原语)。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.clone import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/clone/REPORT.md b/ops/clone/REPORT.md index 2eda0e0..ab2a844 100644 --- a/ops/clone/REPORT.md +++ b/ops/clone/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `clone` | | 语义 | [reference.py](reference.py)(原生 `self.clone`,位级标准) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/gen_golden.py` + `check_accuracy.py` | ✅ 48/48 | [goldendata/](goldendata/README.md) | | 性能 | perf 门禁 | ✅ | [reports/performance.md](reports/performance.md) | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `clone` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.clone import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 关键数字 | 指标 | 自研 Triton | 原生 | diff --git a/ops/clone/reports/development.md b/ops/clone/reports/development.md index a068f39..19c1d80 100644 --- a/ops/clone/reports/development.md +++ b/ops/clone/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(Cambricon MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 | 项 | 值 | diff --git a/ops/clone/reports/test-report.md b/ops/clone/reports/test-report.md index e18e955..cec9b58 100644 --- a/ops/clone/reports/test-report.md +++ b/ops/clone/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `clone` / A1 / Triton 级 | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过**(应用层为等价 torch API 验证) | ## 1. 测试范围 @@ -12,6 +12,8 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py cambricon` | PASS | | 框架层 | ✅ | `test/op_level.py cambricon` | PASS | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | +| 包态执行 | ✅ | `test/op_level.py` 子进程 | `ops.clone.register → ops.clone.kernel`,输出存储独立 | | 应用层 | ✅(等价) | `test/framework_level.py cambricon` | PASS | | 黄金回归 | ✅ | `script/` 两脚本 | 48/48 | | 性能门禁 | ✅ | `scripts/perf_run.py --pattern ops.clone` | 基线可建 | diff --git a/ops/contiguous/README.md b/ops/contiguous/README.md index 28c6b28..e4d9d3b 100644 --- a/ops/contiguous/README.md +++ b/ops/contiguous/README.md @@ -7,7 +7,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`torch.Tensor.contiguous`) | -| 路线 | A1:`Library("aten","IMPL").impl("contiguous", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 设备 | `cambricon`(MLU590-M9) | | 语义 | 已连续返回 self(别名);否则新连续张量;仅覆盖 contiguous_format | @@ -32,4 +32,21 @@ python3 script/bench_perf.py --device mlu --impl triton 3. channels_last 等 memory_format 走原生回退。 4. 参考实现用原生 `.contiguous`:op 层需在注册前取原生参考。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.contiguous import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/contiguous/REPORT.md b/ops/contiguous/REPORT.md index 476353e..a345a32 100644 --- a/ops/contiguous/REPORT.md +++ b/ops/contiguous/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `contiguous` | | 语义 | [reference.py](reference.py)(原生 `.contiguous`,位级标准) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/gen_golden.py` + `check_accuracy.py` | ✅ 114/114 | [goldendata/](goldendata/README.md) | | 性能 | perf 门禁 | ✅ | [reports/performance.md](reports/performance.md) | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `contiguous` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.contiguous import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 关键数字 | 指标 | 自研 Triton | 原生 | diff --git a/ops/contiguous/reports/development.md b/ops/contiguous/reports/development.md index f821d4f..dcdbf4e 100644 --- a/ops/contiguous/reports/development.md +++ b/ops/contiguous/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 | 项 | 值 | diff --git a/ops/contiguous/reports/test-report.md b/ops/contiguous/reports/test-report.md index b8d6fac..40a086e 100644 --- a/ops/contiguous/reports/test-report.md +++ b/ops/contiguous/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `contiguous` / A1 / Triton 级 | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py cambricon` | PASS | | 框架层 | ✅ | `test/op_level.py cambricon` | PASS | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅(等价) | `test/framework_level.py cambricon` | PASS | | 黄金回归 | ✅ | `script/` 两脚本 | 114/114 | | 性能门禁 | ✅ | `scripts/perf_run.py --pattern ops.contiguous` | 可建基线 | diff --git a/ops/copy_/README.md b/ops/copy_/README.md index a55f508..6a5eefd 100644 --- a/ops/copy_/README.md +++ b/ops/copy_/README.md @@ -7,7 +7,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`torch.Tensor.copy_`) | -| 路线 | A1:`Library("aten","IMPL").impl("copy_", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 设备 | `cambricon`(MLU590-M9) | | 语义 | 原地写 dst 并返回 dst;src 支持广播 + cast 到 dst.dtype | @@ -33,4 +33,21 @@ python3 script/bench_perf.py --device mlu --impl triton 4. boundary 溢出到 ±inf 属合法 cast,判定用 isclose。 5. 自拷贝(别名)走原生回退;2D 转置型走分块 swiz。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.copy_ import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/copy_/REPORT.md b/ops/copy_/REPORT.md index 257b3b7..cb2f5f4 100644 --- a/ops/copy_/REPORT.md +++ b/ops/copy_/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `copy_` | | 语义 | [reference.py](reference.py)(原生 `dst.copy_(src)`,原地,位级标准) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/gen_golden.py` + `check_accuracy.py` | ✅ 78/78 | [goldendata/](goldendata/README.md) | | 性能 | perf 门禁 | ✅ | [reports/performance.md](reports/performance.md) | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `copy_` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.copy_ import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 关键数字 | 指标 | 自研 Triton | 原生 | diff --git a/ops/copy_/reports/development.md b/ops/copy_/reports/development.md index 4d71169..4c4f3f9 100644 --- a/ops/copy_/reports/development.md +++ b/ops/copy_/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 | 项 | 值 | diff --git a/ops/copy_/reports/test-report.md b/ops/copy_/reports/test-report.md index aab6e7c..ee69a1f 100644 --- a/ops/copy_/reports/test-report.md +++ b/ops/copy_/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `copy_` / A1 / Triton 级 | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py cambricon` | PASS | | 框架层 | ✅ | `test/op_level.py cambricon` | PASS | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅(等价) | `test/framework_level.py cambricon` | PASS | | 黄金回归 | ✅ | `script/` 两脚本 | 78/78 | | 性能门禁 | ✅ | `scripts/perf_run.py --pattern ops.copy_` | 可建基线 | diff --git a/ops/detach/README.md b/ops/detach/README.md index 6b91826..5750e2e 100644 --- a/ops/detach/README.md +++ b/ops/detach/README.md @@ -26,8 +26,25 @@ python3 script/bench_perf.py --device mlu ## 关键点 -1. 注册:aten::detach@PrivateUse1(拦截 1 次)。 +1. 注册:aten::detach@AutogradPrivateUse1(兼容 PrivateUse1;拦截 1 次)。 2. 判定:共享存储 + requires_grad=False + shape/dtype/stride。 3. 无 Triton/硬件级(该类算子没有可编译的元素级设备码)。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.detach import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/detach/REPORT.md b/ops/detach/REPORT.md index 1546779..b396516 100644 --- a/ops/detach/REPORT.md +++ b/ops/detach/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `detach`(别名/元数据(autograd)类) | | 语义 | [reference.py](reference.py):返回与 self 共享底层存储、脱离 autograd 的新张量 (requires_grad=False) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/` | ✅ 36/36 | | 性能 | 采样 | ✅ 0.0066ms | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `detach` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.detach import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;共享存储 + requires_grad=False + shape/dtype/stride。无 Triton/硬件级(该类算子固有)。 diff --git a/ops/detach/reports/development.md b/ops/detach/reports/development.md index 28f583d..a3b15bf 100644 --- a/ops/detach/reports/development.md +++ b/ops/detach/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/detach/reports/test-report.md b/ops/detach/reports/test-report.md index a86eeb4..e4a9fc8 100644 --- a/ops/detach/reports/test-report.md +++ b/ops/detach/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `detach` / A1 / torch 级(别名/元数据(autograd)) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py` | PASS(25 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | aten::detach@PrivateUse1(拦截 1 次) | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金 | ✅ | `script/` | 36/36 | diff --git a/ops/detach_/README.md b/ops/detach_/README.md index 17979ce..f25df59 100644 --- a/ops/detach_/README.md +++ b/ops/detach_/README.md @@ -30,4 +30,9 @@ python3 script/bench_perf.py --device mlu 2. 判定:原地返回 self + requires_grad=False + 存储不变。 3. 无 Triton/硬件级(该类算子没有可编译的元素级设备码)。 +## 注册契约(2026-10-12) + +`register_a1` 已对齐 `counter` / `platform` 签名,但该算子按设计不做 +A1 注册,调用时保持 `NotImplementedError`。 + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/detach_/REPORT.md b/ops/detach_/REPORT.md index 1d14b0e..f9bfe09 100644 --- a/ops/detach_/REPORT.md +++ b/ops/detach_/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `detach_`(别名/元数据(autograd)类) | | 语义 | [reference.py](reference.py):原地脱离 autograd,返回 self (requires_grad=False) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;register 契约保留,但该算子不可 A1 注册) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **自用/实验** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,12 @@ | 黄金 | `script/` | ✅ 36/36 | | 性能 | 采样 | ✅ 0.0012ms | +## 2026-10-12 注册契约同步 + +- `register_a1` 已对齐 `counter` / `platform` 签名。 +- 该算子保持 `NotImplementedError`,不强行做 A1 注册。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;原地返回 self + requires_grad=False + 存储不变。无 Triton/硬件级(该类算子固有)。 diff --git a/ops/detach_/reports/development.md b/ops/detach_/reports/development.md index 7515520..c4b7b6a 100644 --- a/ops/detach_/reports/development.md +++ b/ops/detach_/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。该算子仍保持 `NotImplementedError`,不强行注册。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/detach_/reports/test-report.md b/ops/detach_/reports/test-report.md index 295267e..ebc4335 100644 --- a/ops/detach_/reports/test-report.md +++ b/ops/detach_/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `detach_` / 自用/实验 / torch 级(别名/元数据(autograd)) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py` | PASS(24 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | —(detach_ 非 backend dispatch,不可 A1 注册) | +| 注册契约 | ✅ | PR #25 / `register.py` | 签名对齐;仍按设计拒绝 A1 注册 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金 | ✅ | `script/` | 36/36 | diff --git a/ops/dropout/README.md b/ops/dropout/README.md index 09815b7..7a29ae1 100644 --- a/ops/dropout/README.md +++ b/ops/dropout/README.md @@ -7,7 +7,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`F.dropout` / `torch.dropout`) | -| 路线 | A1:`Library("aten","IMPL").impl("dropout", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 设备 | `cambricon`(MLU590-M9) | | 语义 | train=False/p=0 别名;p=1 全零;其余 `x*mask/(1-p)` | @@ -35,4 +35,21 @@ python3 script/bench_perf.py --device mlu --impl triton `RuntimeError`——测试 catch 两者。 5. 性能受 RNG 限制,比原生慢 ~5x(与 FlagGems 同级)。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.dropout import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/dropout/REPORT.md b/ops/dropout/REPORT.md index d72bba5..6fcfde8 100644 --- a/ops/dropout/REPORT.md +++ b/ops/dropout/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `dropout` | | 语义 | [reference.py](reference.py)(原生 F.dropout;随机分支按结构/统计判定) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/gen_golden.py` + `check_accuracy.py` | ✅ 189/189 | [goldendata/](goldendata/README.md) | | 性能 | perf 门禁 | ✅ | [reports/performance.md](reports/performance.md) | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `dropout` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.dropout import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 关键数字 | 指标 | 自研 Triton | FlagGems | 原生 | diff --git a/ops/dropout/reports/development.md b/ops/dropout/reports/development.md index cfa15b8..707b16f 100644 --- a/ops/dropout/reports/development.md +++ b/ops/dropout/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 | 项 | 值 | diff --git a/ops/dropout/reports/test-report.md b/ops/dropout/reports/test-report.md index a172c86..6b0d580 100644 --- a/ops/dropout/reports/test-report.md +++ b/ops/dropout/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `dropout` / A1 / Triton 级(Philox) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过**(随机分支按统计判定) | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py cambricon` | PASS | | 框架层 | ✅ | `test/op_level.py cambricon` | PASS | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅(等价) | `test/framework_level.py cambricon` | PASS | | 黄金 | ✅ | `script/` 两脚本 | 189/189 | | 性能门禁 | ✅ | `scripts/perf_run.py --pattern ops.dropout` | 可建基线 | diff --git a/ops/empty/README.md b/ops/empty/README.md index 4648289..f828ea2 100644 --- a/ops/empty/README.md +++ b/ops/empty/README.md @@ -1,6 +1,6 @@ # empty(分配/元数据算子) -`aten::empty` 的 MLU(Cambricon)实现,路线 **A1**, +`aten::empty` 的 MLU(Cambricon)实现,路线 **自用/实验**, **torch 级**(无 Triton / 硬件级,分配算子无数值/设备码)。 ## 定位 @@ -30,4 +30,9 @@ python3 script/bench_perf.py --device mlu 2. 未初始化 → **不能做数值/哨兵断言**,黄金只比元数据。 3. 注册:—。 +## 注册契约(2026-10-12) + +`register_a1` 已对齐 `counter` / `platform` 签名,但该算子按设计不做 +A1 注册,调用时保持 `NotImplementedError`。 + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/empty/REPORT.md b/ops/empty/REPORT.md index bb2d19d..a3bd71f 100644 --- a/ops/empty/REPORT.md +++ b/ops/empty/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `empty`(分配/元数据类) | | 语义 | [reference.py](reference.py):分配 size 形状的未初始化张量,只保证元数据 | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | -| 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;register 契约保留,但该算子不可 A1 注册) | +| 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **自用/实验** | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,12 @@ | 黄金 | `script/` | ✅ 48/48(元数据) | | 性能 | 采样 | ✅ 0.0107ms @8192² fp32 | +## 2026-10-12 注册契约同步 + +- `register_a1` 已对齐 `counter` / `platform` 签名。 +- 该算子保持 `NotImplementedError`,不强行做 A1 注册。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;因是分配算子,黄金只判定元数据(无数值),哨兵 n/a。 diff --git a/ops/empty/reports/development.md b/ops/empty/reports/development.md index edc47bb..2b05ec4 100644 --- a/ops/empty/reports/development.md +++ b/ops/empty/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。该算子仍保持 `NotImplementedError`,不强行注册。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/empty/reports/test-report.md b/ops/empty/reports/test-report.md index 66c3b62..d3839ba 100644 --- a/ops/empty/reports/test-report.md +++ b/ops/empty/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `empty` / A1 / torch 级(分配/元数据) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层(元数据) | ✅ | `test/kernel_level.py` | PASS(15 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | — | +| 注册契约 | ✅ | PR #25 / `register.py` | 签名对齐;仍按设计拒绝 A1 注册 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金(元数据) | ✅ | `script/` | 48/48 | | 数值精度/哨兵 | ⬜ 不适用 | — | 未初始化内存 | diff --git a/ops/empty_like/README.md b/ops/empty_like/README.md index d3faf68..83056de 100644 --- a/ops/empty_like/README.md +++ b/ops/empty_like/README.md @@ -8,7 +8,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`torch.empty_like`) | -| 路线 | A1:`Library("aten","IMPL").impl("empty_like", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 语义 | 未初始化张量;只保证 shape/dtype/stride/device,数值不保证 | | 判定 | **元数据等价**(无数值黄金、无哨兵) | @@ -32,4 +32,21 @@ python3 script/bench_perf.py --device mlu 3. `memory_format=preserve_format`(默认)时 stride 取自 `self`;显式 `contiguous_format`/`channels_last` 时按格式推导。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.empty_like import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/empty_like/REPORT.md b/ops/empty_like/REPORT.md index 348e8cd..c2395af 100644 --- a/ops/empty_like/REPORT.md +++ b/ops/empty_like/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `empty_like`(分配/元数据类) | | 语义 | [reference.py](reference.py):未初始化,只约束 shape/dtype/stride/device | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/` | ✅ 168/168(元数据) | | 性能 | 采样 | ✅ `torch` 0.0133ms @8192² fp32 | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `empty_like` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.empty_like import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;因是分配算子,黄金只判定元数据(无数值),哨兵 n/a。 diff --git a/ops/empty_like/reports/development.md b/ops/empty_like/reports/development.md index 88f7b54..9d6b313 100644 --- a/ops/empty_like/reports/development.md +++ b/ops/empty_like/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/empty_like/reports/test-report.md b/ops/empty_like/reports/test-report.md index 8b55076..d0d5334 100644 --- a/ops/empty_like/reports/test-report.md +++ b/ops/empty_like/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `empty_like` / A1 / torch 级(分配/元数据) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层(元数据) | ✅ | `test/kernel_level.py` | PASS(50 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | PASS(3 次) | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅(等价) | `test/framework_level.py` | PASS | | 黄金(元数据) | ✅ | `script/` | 168/168 | | 数值精度/哨兵 | ⬜ 不适用 | — | 未初始化内存 | diff --git a/ops/empty_strided/README.md b/ops/empty_strided/README.md index 3ed78ba..b09f6d8 100644 --- a/ops/empty_strided/README.md +++ b/ops/empty_strided/README.md @@ -28,6 +28,23 @@ python3 script/bench_perf.py --device mlu 1. 实现:所需存储 empty + as_strided(避免调用自身递归)。 2. 未初始化 → **不能做数值/哨兵断言**,黄金只比元数据。 -3. 注册:aten::empty_strided@PrivateUse1(拦截 2 次)。 +3. 注册:aten::empty_strided@AutogradPrivateUse1(兼容 PrivateUse1;拦截 2 次)。 + +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.empty_strided import register_a1` 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/empty_strided/REPORT.md b/ops/empty_strided/REPORT.md index 3e3275f..12ec77a 100644 --- a/ops/empty_strided/REPORT.md +++ b/ops/empty_strided/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `empty_strided`(分配/元数据类) | | 语义 | [reference.py](reference.py):按指定 stride 分配未初始化张量(允许非连续/重叠 stride) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/` | ✅ 30/30(元数据) | | 性能 | 采样 | ✅ 0.0135ms @8192² fp32 | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `empty_strided` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.empty_strided import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;因是分配算子,黄金只判定元数据(无数值),哨兵 n/a。 diff --git a/ops/empty_strided/reports/development.md b/ops/empty_strided/reports/development.md index a6156b2..c609832 100644 --- a/ops/empty_strided/reports/development.md +++ b/ops/empty_strided/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/empty_strided/reports/test-report.md b/ops/empty_strided/reports/test-report.md index da7c272..0f6a437 100644 --- a/ops/empty_strided/reports/test-report.md +++ b/ops/empty_strided/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `empty_strided` / A1 / torch 级(分配/元数据) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层(元数据) | ✅ | `test/kernel_level.py` | PASS(16 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | aten::empty_strided@PrivateUse1(拦截 2 次) | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金(元数据) | ✅ | `script/` | 30/30 | | 数值精度/哨兵 | ⬜ 不适用 | — | 未初始化内存 | diff --git a/ops/item/README.md b/ops/item/README.md index 332d52c..6ddd476 100644 --- a/ops/item/README.md +++ b/ops/item/README.md @@ -26,8 +26,25 @@ python3 script/bench_perf.py --device mlu ## 关键点 -1. 注册:aten::item@PrivateUse1(拦截 1 次)。 +1. 注册:aten::item@AutogradPrivateUse1(兼容 PrivateUse1;拦截 1 次)。 2. 判定:标量值相等 + Python 类型 + 多元素报错。 3. 无 Triton/硬件级(该类算子没有可编译的元素级设备码)。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.item import register_a1` + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/item/REPORT.md b/ops/item/REPORT.md index b64d329..723d5fb 100644 --- a/ops/item/REPORT.md +++ b/ops/item/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `item`(host 标量类) | | 语义 | [reference.py](reference.py):单元素张量取为 Python 标量 (device→host 同步) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金 | `script/` | ✅ 36/36 | | 性能 | 采样 | ✅ 0.0433ms | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `item` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.item import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;标量值相等 + Python 类型 + 多元素报错。无 Triton/硬件级(该类算子固有)。 diff --git a/ops/item/reports/development.md b/ops/item/reports/development.md index d364d89..c7ce519 100644 --- a/ops/item/reports/development.md +++ b/ops/item/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/item/reports/test-report.md b/ops/item/reports/test-report.md index 141d40c..d3dc319 100644 --- a/ops/item/reports/test-report.md +++ b/ops/item/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `item` / A1 / torch 级(host 标量) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py` | PASS(12 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | aten::item@PrivateUse1(拦截 1 次) | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金 | ✅ | `script/` | 36/36 | diff --git a/ops/result_type/README.md b/ops/result_type/README.md index e27be94..bca576b 100644 --- a/ops/result_type/README.md +++ b/ops/result_type/README.md @@ -30,4 +30,9 @@ python3 script/bench_perf.py --device mlu 2. 判定:公共 dtype 相等。 3. 无 Triton/硬件级(该类算子没有可编译的元素级设备码)。 +## 注册契约(2026-10-12) + +`register_a1` 已对齐 `counter` / `platform` 签名,但该算子按设计不做 +A1 注册,调用时保持 `NotImplementedError`。 + 详见 [REPORT.md](REPORT.md)。 diff --git a/ops/result_type/REPORT.md b/ops/result_type/REPORT.md index 7f36dca..e2086c5 100644 --- a/ops/result_type/REPORT.md +++ b/ops/result_type/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `result_type`(dtype 元数据类) | | 语义 | [reference.py](reference.py):两输入在 PyTorch 类型提升下的公共 dtype | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;register 契约保留,但该算子不可 A1 注册) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **自用/实验** | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,12 @@ | 黄金 | `script/` | ✅ 8/8 | | 性能 | 采样 | ✅ 0.0013ms | +## 2026-10-12 注册契约同步 + +- `register_a1` 已对齐 `counter` / `platform` 签名。 +- 该算子保持 `NotImplementedError`,不强行做 A1 注册。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 结论 三层全绿;公共 dtype 相等。无 Triton/硬件级(该类算子固有)。 diff --git a/ops/result_type/reports/development.md b/ops/result_type/reports/development.md index 909e91f..3d6c94b 100644 --- a/ops/result_type/reports/development.md +++ b/ops/result_type/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。该算子仍保持 `NotImplementedError`,不强行注册。见 PR #25。 + ## 1. 环境配置 MLU590-M9 ×8;torch 2.7.1+cpu / torch_mlu 1.29.2 / triton 3.2.0+mlu / diff --git a/ops/result_type/reports/test-report.md b/ops/result_type/reports/test-report.md index eb5c2d2..d9c2037 100644 --- a/ops/result_type/reports/test-report.md +++ b/ops/result_type/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子 / 路线 / 级别 | `result_type` / 自用/实验 / torch 级(dtype 元数据) | -| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过** | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层 | ✅ | `test/kernel_level.py` | PASS(64 项) | | 框架层(注册/拦截) | ✅ | `test/op_level.py` | —(result_type 为纯函数,不可 A1 注册) | +| 注册契约 | ✅ | PR #25 / `register.py` | 签名对齐;仍按设计拒绝 A1 注册 | | 应用层 | ✅ | `test/framework_level.py` | PASS | | 黄金 | ✅ | `script/` | 8/8 | diff --git a/ops/sdpa_math/REPORT.md b/ops/sdpa_math/REPORT.md index 51e85d0..890184d 100644 --- a/ops/sdpa_math/REPORT.md +++ b/ops/sdpa_math/REPORT.md @@ -8,7 +8,7 @@ | 算子名称 | `aten::_scaled_dot_product_attention_math`(SDPA **math 后端**,双输出 `(out, attn_probs)`) | | 语义 | [reference.py](reference.py)(GQA / causal / bool·float mask / dropout 双规则 / fp32 内部;P800 半精度 fast path 有保守回退) | | 目标设备 / 路线 | `ascend910` + `p800-kunlunxin` + `cpu` / **A1** ☑(aten 拦截,成对注册 `Autograd*` + 纯设备键) | -| 日期 / 状态 | 2026-10-01 / 三平台定稿 | +| 日期 / 状态 | 2026-10-01 定稿 / 2026-10-12 文档复核 | ## 实现矩阵(三级) diff --git a/ops/type_as/README.md b/ops/type_as/README.md index 4a7eb7c..d547e0e 100644 --- a/ops/type_as/README.md +++ b/ops/type_as/README.md @@ -9,7 +9,7 @@ | 项 | 值 | |---|---| | 宿主 | 标准 aten 算子(`torch.Tensor.type_as`) | -| 路线 | A1:`torch.library.Library("aten","IMPL").impl("type_as", fn, "PrivateUse1")` | +| 路线 | A1:`register_a1(dispatch_key="AutogradPrivateUse1", counter=None, platform=None)`;兼容 `PrivateUse1` | | 设备 | `cambricon`(MLU590-M9,PrivateUse1=mlu),profile 见 [configs/devices/cambricon.yaml](../../configs/devices/cambricon.yaml) | | 语义 | `out = self.to(other.dtype)`,留在 `self.device`;同 dtype 返回 `self` | @@ -32,7 +32,7 @@ python3 script/bench_perf.py --device mlu --impl triton ``` 1/3 算子库层 PASS {'ok': True, 'max_err': 0.0, ...} -2/3 框架层 PASS {'ok': True, 'registered': 'aten::type_as@PrivateUse1', ...} +2/3 框架层 PASS {'ok': True, 'registered': 'aten::type_as@AutogradPrivateUse1(兼容 PrivateUse1)', ...} 3/3 应用层 PASS {'ok': True, 'calls': 1, 'output_match': True, ...} ``` @@ -47,4 +47,21 @@ python3 script/bench_perf.py --device mlu --impl triton 5. 本机未装 vLLM,应用层用等价 torch API 的**子进程**验证(注册不跨进程 传播这一核心难点仍被覆盖),vLLM 到位后替换 `_app_child` 即可。 +## 注册契约(2026-10-12) + +`register_a1` 统一支持 `counter` 与显式 `platform`: + +```python +register_a1( + dispatch_key="AutogradPrivateUse1", + counter={"n": 0}, + platform="cambricon", +) +``` + +- canonical key:`AutogradPrivateUse1` +- 兼容旧 profile / 脚本态 key:`PrivateUse1` +- 实现导入:包态优先,单算子脚本态 fallback +- 包入口:`from ops.type_as import register_a1` + 详见 [REPORT.md](REPORT.md) 与 [reports/](reports/)。 diff --git a/ops/type_as/REPORT.md b/ops/type_as/REPORT.md index c61bff1..581fd49 100644 --- a/ops/type_as/REPORT.md +++ b/ops/type_as/REPORT.md @@ -4,9 +4,9 @@ |---|---| | 算子名称 | `type_as`(`self` cast 到 `other.dtype`,留在 `self.device`) | | 语义 | [reference.py](reference.py)(CPU 原生 `.to`,位级判卷标准) | -| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu / PrivateUse1) | +| 硬件平台 | **1 个**:Cambricon MLU590(profile `cambricon`,torch_mlu;A1 canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`) | | 目标设备 / 路线 | `cambricon`(MLU590-M9)/ **A1**(aten `type_as` 拦截) | -| 日期 / 状态 | 2026-09-18 / 定稿 | +| 日期 / 状态 | 2026-09-18 定稿 / 2026-10-12 注册契约同步 | ## 实现矩阵(三级) @@ -27,6 +27,15 @@ | 黄金回归 | `script/gen_golden.py` + `check_accuracy.py` | ✅ 111/111 | [goldendata/](goldendata/README.md) | | 性能 | `scripts/perf_run.py` / 基线门禁 | ✅ | [reports/performance.md](reports/performance.md) | +## 2026-10-12 注册契约同步 + +- `register_a1` 支持 `counter["n"]` 拦截计数与显式 `platform`。 +- canonical key 为 `AutogradPrivateUse1`,`PrivateUse1` 作为旧 profile / + 单算子脚本态兼容 key。 +- `type_as` 的实现导入为包态优先、脚本态 fallback,包入口使用 + `from ops.type_as import register_a1`。 +- 复验见 PR #25:MLU 13/13 三层全绿、unit tests 与真实 vLLM A1 注入通过。 + ## 关键数字 | 指标 | 自研 Triton | 原生 ATen | FlagGems `to_copy` | diff --git a/ops/type_as/reports/development.md b/ops/type_as/reports/development.md index 24365c1..ac2c817 100644 --- a/ops/type_as/reports/development.md +++ b/ops/type_as/reports/development.md @@ -7,6 +7,8 @@ | 目标设备 | `cambricon`(Cambricon MLU590-M9) | | 日期 / 状态 | 2026-09-18 / 定稿 | +> **2026-10-12 维护记录**:`register_a1` 已升级为 `counter + platform` 契约;canonical key 为 `AutogradPrivateUse1`,历史 `PrivateUse1` 结论保留为兼容 key。实现导入已改为包态优先、单算子脚本态 fallback。见 PR #25。 + ## 1. 环境配置 ### 1.1 硬件 diff --git a/ops/type_as/reports/test-report.md b/ops/type_as/reports/test-report.md index ccdb39f..0a04906 100644 --- a/ops/type_as/reports/test-report.md +++ b/ops/type_as/reports/test-report.md @@ -3,7 +3,7 @@ | 项 | 值 | |---|---| | 算子名称 / 路线 / 级别 | `type_as` / A1 / Triton 级(+ torch 级对照) | -| 目标设备 / 测试日期 | `cambricon`(MLU590-M9)/ 2026-09-18 | +| 目标设备 / 测试日期 | `cambricon`(MLU590-M9)/ 2026-09-18;2026-10-12 契约复验 | | 结论 | **通过**(应用层为等价 torch API 验证,非真实 vLLM) | ## 1. 测试范围 @@ -12,6 +12,7 @@ |---|---|---|---| | kernel 层(精度/哨兵/性能) | ✅ | `test/kernel_level.py cambricon` | PASS | | 框架层(注册/拦截) | ✅ | `test/op_level.py cambricon` | PASS | +| 注册契约 | ✅ | PR #25 / `register.py` | `counter` + `platform`;canonical `AutogradPrivateUse1`,兼容 `PrivateUse1`;包态导入优先 | | 应用层 | ✅(等价) | `test/framework_level.py cambricon` | PASS(vLLM 未安装) | | 黄金回归 | ✅ | `script/gen_golden.py` + `check_accuracy.py` | 111/111 | | 性能回归门禁 | ✅ | `scripts/perf_compare.py --device cambricon` | 基线已建 | diff --git a/scripts/build_ops_index.py b/scripts/build_ops_index.py new file mode 100644 index 0000000..28c1611 --- /dev/null +++ b/scripts/build_ops_index.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 +"""Validate ops/catalog.yaml and generate ops/README.md. + +The catalog is the scalable inventory source for a growing operator/platform +matrix. The generated README is checked in for direct GitHub browsing. +""" +from __future__ import annotations + +import argparse +import sys +from collections import Counter +from pathlib import Path + +import yaml + + +ROOT = Path(__file__).resolve().parents[1] +CATALOG_PATH = ROOT / "ops" / "catalog.yaml" +OUTPUT_PATH = ROOT / "ops" / "README.md" +EXPECTED_SCHEMA = 1 +VALID_PLATFORM_STATUSES = {"partial", "planned"} +VALID_GROUP_KINDS = {"core", "foundation"} + + +def load_catalog(path: Path = CATALOG_PATH) -> dict: + return yaml.safe_load(path.read_text()) + + +def _require_mapping(value, name: str) -> dict: + if not isinstance(value, dict) or not value: + raise ValueError(f"{name} must be a non-empty mapping") + return value + + +def validate_catalog(catalog: dict, root: Path = ROOT) -> None: + """Validate schema, filesystem links, and incremental coverage semantics.""" + if catalog.get("schema") != EXPECTED_SCHEMA: + raise ValueError(f"ops/catalog.yaml schema must be {EXPECTED_SCHEMA}") + + families = _require_mapping(catalog.get("platform_families"), "platform_families") + groups = _require_mapping(catalog.get("operator_groups"), "operator_groups") + operators = catalog.get("operators") + if not isinstance(operators, list) or not operators: + raise ValueError("operators must be a non-empty list") + + expected_count = catalog.get("target_scope", {}).get("vendor_family_count") + if expected_count != len(families): + raise ValueError( + "target_scope.vendor_family_count must equal " + f"{len(families)}, got {expected_count!r}" + ) + + ranks: set[int] = set() + for group, meta in groups.items(): + if meta.get("kind") not in VALID_GROUP_KINDS: + raise ValueError(f"operator group {group} has unknown kind") + rank = meta.get("rank") + if not isinstance(rank, int): + raise ValueError(f"operator group {group} lacks integer rank") + if rank in ranks: + raise ValueError("operator group ranks must be unique") + ranks.add(rank) + for field in ("title", "description"): + if not isinstance(meta.get(field), str) or not meta[field]: + raise ValueError(f"operator group {group} lacks {field}") + + family_counts: Counter[str] = Counter() + for family, meta in families.items(): + if meta.get("status") not in VALID_PLATFORM_STATUSES: + raise ValueError( + f"unknown status for platform {family}: {meta.get('status')!r}" + ) + if not isinstance(meta.get("display_name"), str) or not meta["display_name"]: + raise ValueError(f"platform {family} lacks display_name") + profiles = meta.get("current_profiles") + if not isinstance(profiles, list): + raise ValueError(f"platform {family} current_profiles must be a list") + for profile in profiles: + if not (root / "configs" / "devices" / f"{profile}.yaml").is_file(): + raise ValueError( + f"platform {family} profile has no configs/devices/{profile}.yaml" + ) + if meta["status"] == "planned" and profiles: + raise ValueError(f"planned platform {family} cannot declare profiles") + + names: set[str] = set() + directories: set[str] = set() + used_groups: set[str] = set() + for op in operators: + name = op.get("name") + directory = op.get("directory") + group = op.get("group") + if not isinstance(name, str) or not name: + raise ValueError("every operator needs a name") + if name in names: + raise ValueError(f"duplicate operator name: {name}") + if not isinstance(directory, str) or not directory: + raise ValueError(f"operator {name} lacks directory") + if directory in directories: + raise ValueError(f"duplicate operator directory: {directory}") + if group not in groups: + raise ValueError(f"operator {name} has unknown group {group!r}") + for field in ("purpose", "key_result"): + if not isinstance(op.get(field), str) or not op[field]: + raise ValueError(f"operator {name} lacks {field}") + if groups[group]["kind"] == "foundation" and not op.get("route"): + raise ValueError(f"foundation operator {name} lacks route") + if not (root / "ops" / directory).is_dir(): + raise ValueError(f"operator directory does not exist: {directory}") + + platforms = _require_mapping(op.get("platforms"), f"{name}.platforms") + for family, platform in platforms.items(): + if family not in families: + raise ValueError(f"operator {name} has unknown family {family!r}") + if families[family]["status"] == "planned": + raise ValueError( + f"operator {name} declares {family}, but that family is planned" + ) + profiles = platform.get("profiles") + if not isinstance(profiles, list) or not profiles: + raise ValueError(f"operator {name}/{family} lacks profiles") + family_profiles = set(families[family]["current_profiles"]) + unknown_profiles = sorted(set(profiles) - family_profiles) + if unknown_profiles: + raise ValueError( + f"operator {name}/{family} has profiles outside its family: " + f"{unknown_profiles}" + ) + family_counts[family] += 1 + if len(platforms) > 1: + operator_dir = root / "ops" / directory + if not (operator_dir / "PLATFORM.md").is_file(): + raise ValueError(f"multi-platform operator {name} lacks PLATFORM.md") + if not (operator_dir / "MERGE.md").is_file(): + raise ValueError(f"multi-platform operator {name} lacks MERGE.md") + names.add(name) + directories.add(directory) + used_groups.add(group) + + unused_groups = sorted(set(groups) - used_groups) + if unused_groups: + raise ValueError(f"operator groups have no operators: {unused_groups}") + for family, meta in families.items(): + if meta["status"] == "partial" and family_counts[family] == 0: + raise ValueError(f"partial platform {family} has no delivered operators") + + for contract_name, contract in catalog.get("registration_contracts", {}).items(): + for field in ( + "title", "scope", "canonical_key", "compatibility_key" + ): + if not isinstance(contract.get(field), str) or not contract[field]: + raise ValueError(f"registration contract {contract_name} lacks {field}") + if not isinstance(contract.get("registerable_count"), int): + raise ValueError( + f"registration contract {contract_name} lacks registerable_count" + ) + + actual_directories = { + path.name for path in (root / "ops").iterdir() + if path.is_dir() and path.name != "__pycache__" + } + missing = sorted(actual_directories - directories) + if missing: + raise ValueError(f"operator directories missing from catalog: {missing}") + + +def _operator_link(op: dict) -> str: + return f"[{op['name']}]({op['directory']}/)" + + +def _platform_summary(catalog: dict) -> list[str]: + counts = Counter( + family + for op in catalog["operators"] + for family in op["platforms"] + ) + lines = [ + "| 目标平台 | 当前状态 | 已登记算子 | 当前 profile / 型号 |", + "|---|---|---:|---|", + ] + for family, meta in catalog["platform_families"].items(): + profiles = [f"`{item}`" for item in meta.get("current_profiles", [])] + variants = meta.get("known_variants", []) + if variants: + profiles.append("计划覆盖:" + " / ".join(variants)) + if not profiles: + profiles = ["待首个交付算子确定"] + status = "部分交付" if meta["status"] == "partial" else "规划中" + lines.append( + f"| {meta['display_name']}(`{family}`) | {status} | " + f"{counts[family]} | {' · '.join(profiles)} |" + ) + return lines + + +def _core_rows(catalog: dict) -> list[str]: + families = catalog["platform_families"] + rows = [] + for op in catalog["operators"]: + if catalog["operator_groups"][op["group"]]["kind"] != "core": + continue + platforms = " · ".join( + f"{families[family]['display_name']}" + f"({' / '.join(platform['profiles'])})" + for family, platform in op["platforms"].items() + ) + rows.append( + f"| {_operator_link(op)} | {op['purpose']} | {platforms} | " + f"{op['key_result']} |" + ) + return rows + + +def _foundation_rows(catalog: dict, group: str) -> list[str]: + rows = [] + for op in catalog["operators"]: + if op["group"] != group: + continue + routes = [] + for platform in op["platforms"].values(): + route = platform.get("route") or op.get("route") + if route and route not in routes: + routes.append(route) + route = " / ".join(routes) or "—" + rows.append( + f"| {_operator_link(op)} | {op['purpose']} | {route} | " + f"{op['key_result']} |" + ) + return rows + + +def render_index(catalog: dict) -> str: + operators = catalog["operators"] + groups = catalog["operator_groups"] + core_count = sum( + groups[op["group"]]["kind"] == "core" for op in operators + ) + foundation_count = len(operators) - core_count + lines: list[str] = [ + "# 已开发算子(ops/)", + "", + "", + "", + "这个目录存放按样板交付的算子。[ops/catalog.yaml](catalog.yaml) 是算子清单和平台", + "覆盖的唯一事实来源;本页由脚本生成,避免手工维护长表格时漏改,也", + "避免把局部平台结论误写成全局结论。", + "维护方法和数据模型见[算子目录维护指南](../docs/operator-catalog.md)。", + "", + "## 新算子放进来", + "", + "```bash", + "cp -r templates/operator ops/<你的算子名>/", + "cd ops/<你的算子名>/", + "grep -rl my_op . | xargs sed -i 's/my_op/<你的算子名>/g'", + "```", + "", + "开发流程照[全链路指南](../docs/fullstack-guide.md)七步走。完成后:", + "", + "1. 运行 `python3 example.py `,确认三层测试全绿。", + "2. 补齐 REPORT 和 development / test-report / accuracy / performance 分册。", + "3. 在 `ops/catalog.yaml` 登记算子、用途、平台和证据。", + "4. 运行 `python3 scripts/build_ops_index.py` 更新本页。", + "5. 交付路径选择见[集成指南](../docs/integration.md)。", + "", + "## 平台覆盖口径", + "", + f"目标范围是 **{len(catalog['platform_families'])} 个厂商平台家族**。" + f"当前登记 **{len(operators)} 个算子**:**{core_count} 个核心多平台算子**、" + f"**{foundation_count} 个基础 ATen 算子**。", + "这是一个增量交付子集,不是完整平台矩阵。`planned` 只表示目标范围,", + "不表示支持完成;只有进入算子条目并带有测试与报告的平台才计为已交付。", + "", + *_platform_summary(catalog), + "", + ] + + ranked = sorted(groups.items(), key=lambda item: (item[1]["rank"], item[0])) + core_groups = [(name, meta) for name, meta in ranked if meta["kind"] == "core"] + foundation_groups = [ + (name, meta) for name, meta in ranked if meta["kind"] == "foundation" + ] + + for name, meta in core_groups: + lines.extend([ + f"## {meta['title']}", + "", + meta["description"].strip(), + "", + "| 算子 | 作用 | 平台 | 关键结论 |", + "|---|---|---|---|", + *_core_rows(catalog), + "", + ]) + + if foundation_groups: + lines.extend([ + "## 基础 ATen 算子", + "", + "这些算子主要验证基础语义、执行链路、A1 注册和真实框架注入。", + "它们不一定以性能提升为首要目标,因此按用途分组,避免和核心计算", + "算子混在同一张长表里。", + "", + ]) + for name, meta in foundation_groups: + lines.extend([ + f"### {meta['title']}", + "", + meta["description"].strip(), + "", + "| 算子 | 作用 | 交付路线 | 关键结论 |", + "|---|---|---|---|", + *_foundation_rows(catalog, name), + "", + ]) + + lines.extend([ + "## 平台注册契约", + "", + "注册契约按平台族记录,不把某个平台的 dispatch key 推广成全局规则。", + "", + ]) + for contract in catalog.get("registration_contracts", {}).values(): + non_registerable = "、".join( + f"`{name}`" + for name in contract["non_registerable_operators"] + ) + lines.extend([ + f"### {contract['title']}", + "", + f"适用范围:{contract['scope']}", + "", + "```python", + "register_a1(", + f" dispatch_key=\"{contract['canonical_key']}\",", + " counter: dict | None = None,", + " platform=,", + ")", + "```", + "", + f"- Canonical key:`{contract['canonical_key']}`", + f"- Compatibility key:`{contract['compatibility_key']}`", + f"- 可注册算子:{contract['registerable_count']}", + f"- 不可注册算子:{non_registerable}", + *[f"- {note}" for note in contract.get("notes", [])], + "", + ]) + + lines.extend([ + "## 维护规则", + "", + "- **目录名 = 算子名**,使用小写下划线。", + "- **新增平台家族**先在 catalog 登记 display name、status 和 roadmap,", + " 再随首个算子交付 profile;不要提前把规划平台写进 operator 条目。", + "- **新增算子**同步更新 catalog、三层测试、golden、性能数据和报告。", + "- **多平台算子**必须维护 `PLATFORM.md`;涉及平台合并流程时维护 `MERGE.md`。", + "- **平台结论限定范围**:dtype、shape、dispatch key、运行时版本和", + " fallback 边界写在同一份证据链里。", + "- **包态导入**统一使用 `from ops. import register_a1`;脚本态", + " 仅作为单算子目录内运行的兼容模式。", + "- CI 的 `ops-packaging` 是 CPU 包入口扫描;`ops/test_packaging.py` 是", + " NPU 真机 E2E,两者口径不同。", + "- 修改 catalog 后必须运行:", + "", + " ```bash", + " python3 scripts/build_ops_index.py", + " ```", + "", + "- 合入前照[交付自查清单](../templates/operator/README.md)检查。", + "", + "", + "", + ]) + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", action="store_true", + help="fail when ops/README.md is out of date") + args = parser.parse_args() + + catalog = load_catalog() + validate_catalog(catalog) + rendered = render_index(catalog) + if args.check: + current = OUTPUT_PATH.read_text() if OUTPUT_PATH.exists() else "" + if current != rendered: + print("ops/README.md is out of date. Run scripts/build_ops_index.py.") + return 1 + print("ops/README.md is up to date.") + return 0 + + OUTPUT_PATH.write_text(rendered) + print(f"generated -> {OUTPUT_PATH}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/templates/operator/README.md b/templates/operator/README.md index c4eb4c3..3e7ed7e 100644 --- a/templates/operator/README.md +++ b/templates/operator/README.md @@ -118,6 +118,8 @@ grep -rl my_op . | xargs sed -i 's/my_op/<你的算子名>/g' - [ ] 三层测试: test/{kernel,op,framework}_level.py 全绿 - [ ] 性能对照: script/bench_perf.py(native 参照必备)+ reports/ 四件套 - [ ] 黄金: goldendata/ + script/check_accuracy.py 全过 +- [ ] 算子目录: `ops/catalog.yaml` 已登记,且 + `python3 scripts/build_ops_index.py --check` 通过 - [ ] 包化回归: `ops/test_packaging.py` 全算子导入扫描通过 - [ ] 多平台时: PLATFORM.md(绑定清单)+ MERGE.md(七步流程) - [ ] 署名: 改动处 `wt-YYYY-MM-DD-fix` + `# wt <邮箱>`,commit 作者一致 diff --git a/tests/unit/test_ops_catalog.py b/tests/unit/test_ops_catalog.py new file mode 100644 index 0000000..d2d48da --- /dev/null +++ b/tests/unit/test_ops_catalog.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from pathlib import Path + +import yaml + +from scripts.build_ops_index import ( + OUTPUT_PATH, + load_catalog, + render_index, + validate_catalog, +) + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_catalog_models_six_incremental_platform_families(): + catalog = load_catalog() + assert catalog["target_scope"]["vendor_family_count"] == 6 + assert set(catalog["platform_families"]) == { + "ascend", "cambricon", "kunlunxin", "thead", "enflame", "biren" + } + + planned = { + name: meta + for name, meta in catalog["platform_families"].items() + if meta["status"] == "planned" + } + assert set(planned) == {"thead", "enflame", "biren"} + assert all(not meta["current_profiles"] for meta in planned.values()) + + +def test_catalog_is_valid_and_covers_every_operator_directory(): + catalog = load_catalog() + validate_catalog(catalog) + + actual = { + path.name for path in (ROOT / "ops").iterdir() + if path.is_dir() and path.name != "__pycache__" + } + cataloged = {op["directory"] for op in catalog["operators"]} + assert cataloged == actual + + +def test_generated_ops_index_is_current(): + catalog = load_catalog() + validate_catalog(catalog) + assert OUTPUT_PATH.read_text() == render_index(catalog) + + +def test_platform_claims_are_incremental_and_backed_by_profiles(): + catalog = load_catalog() + operators_by_family = { + family: [ + op for op in catalog["operators"] if family in op["platforms"] + ] + for family in catalog["platform_families"] + } + + for family, meta in catalog["platform_families"].items(): + if meta["status"] == "planned": + assert operators_by_family[family] == [] + else: + assert operators_by_family[family] + assert meta["current_profiles"] + + +def test_catalog_yaml_is_parseable_and_versioned(): + data = yaml.safe_load((ROOT / "ops/catalog.yaml").read_text()) + assert data["schema"] == 1