From 8d42d958e4cee6e60af6ac28ee8fb888f5b187e3 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 9 Sep 2026 10:33:22 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20#38:=20=E8=A1=A5=E5=85=A8=E6=98=87?= =?UTF-8?q?=E8=85=BE950=E7=B3=BB=E5=88=97=E7=A1=AC=E4=BB=B6=E8=A7=84?= =?UTF-8?q?=E6=A0=BC=20(=E6=96=B0=E5=A2=9E=20Ascend950DT=20=E7=AD=89=204?= =?UTF-8?q?=20=E4=B8=AA=20SKU),=20=E9=BB=98=E8=AE=A4=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 数据源: 昇腾950_NPU架构白皮书 表3-1 (系列 SKU) / 表4-2 (Memory 层次) - 新增 hardware/ascend950dt.py: ASCEND950DT (36AIC/72AIV, HBM 4TB/s 144GB, L2 128MB 主bin) + ASCEND950DT_C32 + ASCEND950DT_C28 - ascend950pr.py: 新增 ASCEND950PR_C28 (28核, 1.4TB/s, L2 112MB) 与 gm_capacity_gb 信息字段; 注明 cube_peak_tflops 口径 (= 白皮书 "Cube+Vector 总算力" 行, 与 Cube 单行 432 的 12.5% 偏差待用户裁决) - __init__.py: SPECS 注册表 + get_spec(name), 默认仍为 ASCEND950PR, 现有 import 与调用点零改动 - 共架构交叉验证: 单核 Cube 13.5T (432/32=486/36), Vector fp32 27T/64核≈30T/72核 -> 128lane@1.65GHz; 表4-2 L1/L0/UB 各档一致 - docs/01_软件架构 + README 硬件层说明更新 - tests: TestIssue38 六例 (默认不变/注册表/DT派生量/AIV白皮书验证/DT路由 冒烟/未知KeyError); 87/87 通过; examples 44例 0 diff; 压力回归 10000 例干净, 分支分布与上轮逐数一致 --- BMM/BMM_Theory/README.md | 3 +- .../bmm_theory/hardware/__init__.py | 25 ++++++- .../bmm_theory/hardware/ascend950dt.py | 52 +++++++++++++ .../bmm_theory/hardware/ascend950pr.py | 25 ++++++- BMM/BMM_Theory/docs/01_软件架构.md | 8 +- BMM/BMM_Theory/tests/test_branches.py | 74 +++++++++++++++++++ 6 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 BMM/BMM_Theory/bmm_theory/hardware/ascend950dt.py diff --git a/BMM/BMM_Theory/README.md b/BMM/BMM_Theory/README.md index ad6feeb..50d8d4e 100644 --- a/BMM/BMM_Theory/README.md +++ b/BMM/BMM_Theory/README.md @@ -68,7 +68,8 @@ BMM_Theory/ ├── bmm_theory/ # 软件包 │ ├── __main__.py # CLI 入口 (recommend / evaluate) │ ├── models.py # 数据模型: BmmCase / ImplPlan(标准结构体) / HardwareTiming -│ ├── hardware/ascend950pr.py# 950PR 硬件参数表 (换芯片只换这份) +│ ├── hardware/ # 硬件参数表 (NpuSpec; 950PR 32/28核 + 950DT 36/32/28核, +│ │ # SPECS/get_spec 索引, 默认 ASCEND950PR; 换芯片只加这份) │ ├── timing.py # 时延评估引擎 (MTE2/Cube/Fixpipe/Reduce 模型) │ ├── constraints.py # 单一约束源 (生成与校验共用, L0C/L0A/L0B/L1/dValue/核数) │ ├── router.py # 分支决策路由 + 重叠区仲裁 + 生成后自检 diff --git a/BMM/BMM_Theory/bmm_theory/hardware/__init__.py b/BMM/BMM_Theory/bmm_theory/hardware/__init__.py index 74894d2..1ed4c69 100644 --- a/BMM/BMM_Theory/bmm_theory/hardware/__init__.py +++ b/BMM/BMM_Theory/bmm_theory/hardware/__init__.py @@ -1,3 +1,24 @@ -from .ascend950pr import ASCEND950PR, NpuSpec +from .ascend950pr import ASCEND950PR, ASCEND950PR_C28, NpuSpec +from .ascend950dt import ASCEND950DT, ASCEND950DT_C32, ASCEND950DT_C28 -__all__ = ["ASCEND950PR", "NpuSpec"] +# 芯片规格注册表 (issue#38): name -> NpuSpec; 默认主 bin = ASCEND950PR +SPECS = {s.name: s for s in ( + ASCEND950PR, ASCEND950PR_C28, + ASCEND950DT, ASCEND950DT_C32, ASCEND950DT_C28, +)} + + +def get_spec(name: str = "Ascend950PR") -> NpuSpec: + """按名称取芯片规格 (默认 Ascend950PR 32 核主 bin); 未知名称抛 KeyError.""" + try: + return SPECS[name] + except KeyError: + raise KeyError(f"未知芯片规格 {name!r}, 可选: {sorted(SPECS)}") from None + + +__all__ = [ + "NpuSpec", + "ASCEND950PR", "ASCEND950PR_C28", + "ASCEND950DT", "ASCEND950DT_C32", "ASCEND950DT_C28", + "SPECS", "get_spec", +] diff --git a/BMM/BMM_Theory/bmm_theory/hardware/ascend950dt.py b/BMM/BMM_Theory/bmm_theory/hardware/ascend950dt.py new file mode 100644 index 0000000..6f279f4 --- /dev/null +++ b/BMM/BMM_Theory/bmm_theory/hardware/ascend950dt.py @@ -0,0 +1,52 @@ +"""Ascend950DT 硬件规格参数 (issue#38). + +数值来源: 昇腾950 NPU架构白皮书 表3-1 (系列 SKU 规格) / 表4-2 (Memory 层次); +产品形态: Atlas 850E 超节点 / Atlas 650E 服务器 / Atlas 950 SuperPoD 超节点. + +与 950PR **共架构** (第三代 DaVinci), 仅以下维度不同: + - AI 子系统 36/32/28 个 (每子系统 = 1 Cube Core + 2 Vector Core); + - 高速片上内存 4TB/s (950PR 为 1.6/1.4TB/s), 容量 144/96GB; + - L2 128MB 全档一致 (950PR 28 核降 112MB). + +共架构的交叉验证 (表3-1/表4-2 反推, 与 950PR 单核速率一致): + - Cube: 432T/32核 = 486T/36核 = 13.5T/核 (= 16^3 MAC/拍 @ ~1.65GHz); + 本表 cube_peak_tflops 沿用 NpuSpec 现行口径 (= 白皮书 "Cube+Vector + 总算力" 行, 单核 15.1875T 全系列一致, 口径说明见 ascend950pr.py); + - Vector fp32: 27T/64核 ≈ 30T/72核 -> 128 lane/拍 @ 1.65GHz (乘加计 2 次), + 即 aiv_freq_ghz / aiv_fp32_per_cycle 默认值直接适用; + - L1 512KB / L0A/L0B 64KB / L0C 256KB / UB 512KB 每核一致 (表4-2); + - L2 带宽白皮书未分档, 沿用 5.2TB/s 待标定; 搬移效率经验值 + (dValue/minTile/minDatamountPerCore/T_cmd=0) 同架构沿用 950PR 口径. +""" + +from __future__ import annotations + +from .ascend950pr import NpuSpec + +# 950DT 36 核主 bin (白皮书表3-1: 36 AIC / 72 AIV, HBM 4TB/s 144GB, L2 128MB) +ASCEND950DT = NpuSpec( + name="Ascend950DT", + aic_num=36, aiv_num=72, + cube_peak_tflops=547.0, + bw_gm=4.0e12, + gm_capacity_gb=144.0, +) + +# 950DT 32 核档 (HBM 4TB/s 不变; 容量表3-1 仅给 144/96 两档, 32 核档映射待确认) +ASCEND950DT_C32 = NpuSpec( + name="Ascend950DT_C32", + aic_num=32, aiv_num=64, + cube_peak_tflops=486.0, + bw_gm=4.0e12, + gm_capacity_gb=144.0, +) + +# 950DT 28 核档 (HBM 4TB/s 不变, 容量 96GB; 总算力 425 为白皮书取整值, +# 单核 15.179T 与主 bin 15.1875T 差 0.06% 属取整误差) +ASCEND950DT_C28 = NpuSpec( + name="Ascend950DT_C28", + aic_num=28, aiv_num=56, + cube_peak_tflops=425.0, + bw_gm=4.0e12, + gm_capacity_gb=96.0, +) diff --git a/BMM/BMM_Theory/bmm_theory/hardware/ascend950pr.py b/BMM/BMM_Theory/bmm_theory/hardware/ascend950pr.py index 1440f38..a080514 100644 --- a/BMM/BMM_Theory/bmm_theory/hardware/ascend950pr.py +++ b/BMM/BMM_Theory/bmm_theory/hardware/ascend950pr.py @@ -1,7 +1,8 @@ """Ascend950PR (DAV_3510) 硬件规格参数. -数值来源: 《BMM算子优化分析 v0.98》§二 + 昇腾950 NPU架构白皮书. -换芯片时逻辑结构不变, 只需新增一份同结构参数表. +数值来源: 《BMM算子优化分析 v0.98》§二 + 昇腾950 NPU架构白皮书 (表3-1 SKU 规格 / +表4-2 Memory 层次). 换芯片时逻辑结构不变, 只需新增一份同结构参数表 +(系列其他 SKU 见文末 ASCEND950PR_C28 与 hardware/ascend950dt.py, issue#38). 单位约定: 算力 FLOP/s, 带宽 Byte/s, 容量 Byte, 时延 秒. @@ -10,6 +11,11 @@ dtype 感知算力 (issue#28, 设计文档 docs/05 §2): 提供 2x FP16 张量 TFLOPS, MXFP4 提供 4x FP16; FP32/TF32 同代比值为假设值 (按 DaVinci 惯例 = 1/2, 白皮书未给同代比值), int8 假设同 FP8, 均待实测标定. AIV 逐元素通量按 lane 位宽等比假设 (16bit x2 / 8bit x4 / 4bit x8, 待标定). + +口径注意 (issue#38): cube_peak_tflops 沿用 v0.98/用户澄清口径 = 白皮书表3-1 + "Cube+Vector 总算力" 行 (950PR 32核 = 486), 单核 15.1875T; 白皮书 "Cube算力" + 单行为 Cube 单元独立口径 (950PR 32核 = 432, 单核 13.5T)。两者差 12.5%, + 是否切换全模型口径待用户裁决 (本字段语义不变, 新增 SKU 同口径). """ from __future__ import annotations @@ -54,8 +60,9 @@ class NpuSpec: # ---- L2 / GM ---- l2_bytes: int = 128 * 1024 * 1024 # L2 Cache 128MB - bw_l2: float = 5.2e12 # L2 读写带宽 5.2TB/s + bw_l2: float = 5.2e12 # L2 读写带宽 5.2TB/s (白皮书未给, 用户澄清口径) bw_gm: float = 1.6e12 # GM(HBM) 带宽 1.6TB/s (读写共享) + gm_capacity_gb: float = 128.0 # GM(HBM) 容量 GB (信息项, 模型暂不消费) # ---- 搬移效率经验约束 (950PR 实测总结) ---- dvalue_recommend: int = 512 # dValue 推荐值 (Byte), 更大无额外收益 @@ -164,3 +171,15 @@ class NpuSpec: ASCEND950PR = NpuSpec() + +# 950PR 28 核衍生版 (白皮书表3-1: 28 AIC / 56 AIV, HBM 1.4TB/s 112GB, +# L2 112MB; L2 带宽未分档, 沿用 5.2TB/s 待标定; 总算力 425 为白皮书取整值, +# 单核 15.179T 与主 bin 15.1875T 差 0.06% 属取整误差) +ASCEND950PR_C28 = NpuSpec( + name="Ascend950PR_C28", + aic_num=28, aiv_num=56, + cube_peak_tflops=425.0, + l2_bytes=112 * 1024 * 1024, + bw_gm=1.4e12, + gm_capacity_gb=112.0, +) diff --git a/BMM/BMM_Theory/docs/01_软件架构.md b/BMM/BMM_Theory/docs/01_软件架构.md index 06cb4c9..9522a75 100644 --- a/BMM/BMM_Theory/docs/01_软件架构.md +++ b/BMM/BMM_Theory/docs/01_软件架构.md @@ -25,7 +25,9 @@ │ └─ constraints.py 单一约束源 (生成与校验共用) │ ├─────────────────────────────────────────────────────────┤ │ 硬件层 (hardware/) │ -│ └─ ascend950pr.py NpuSpec 参数表 (换芯片只换这份) │ +│ ├─ ascend950pr.py NpuSpec + 950PR 32/28核 SKU │ +│ ├─ ascend950dt.py 950DT 36/32/28核 SKU (issue#38) │ +│ └─ __init__.py SPECS 注册表 + get_spec(name) │ ├─────────────────────────────────────────────────────────┤ │ IO 层 (io_csv.py) case/plan/result 的 csv 读写 │ └─────────────────────────────────────────────────────────┘ @@ -86,7 +88,7 @@ class Branch: ### 3.3 硬件参数与逻辑分离 -`hardware/ascend950pr.py` 的 `NpuSpec` 集中所有芯片常数(核数/算力/各级容量/带宽/搬移效率经验值/T_cmd)。所有分支通过 `self.spec` 取参数——换芯片时新增一份参数表即可,分支逻辑零改动。 +`hardware/` 的 `NpuSpec` 集中所有芯片常数(核数/算力/各级容量/带宽/搬移效率经验值/T_cmd)。所有分支通过 `self.spec` 取参数——换芯片时新增一份参数表即可,分支逻辑零改动。已注册 SKU(issue#38,昇腾950 白皮书表3-1/表4-2):`Ascend950PR`(32核主bin)/`Ascend950PR_C28` 与 `Ascend950DT`(36核主bin)/`Ascend950DT_C32`/`Ascend950DT_C28`,经 `hardware.SPECS` / `get_spec(name)` 索引,默认仍为 `ASCEND950PR`。 ### 3.4 时延引擎统一在 timing.py @@ -110,6 +112,6 @@ class Branch: 当前六分支已全部实现(转Matmul / 特殊 / MergeBatch / IterBatch / StreamK / ASW_Basic)。后续方向: 1. **转Matmul 精切**: 当前折叠后只粗估(`to_matmul.py::evaluate` 按 Matmul 总量),接入 MM 理论体系做精确切分。 -2. **换芯片**: 复制 `hardware/ascend950pr.py` 改常数,`NpuSpec` 接口不变。 +2. **换芯片**: `NpuSpec(name=..., ...)` 派生实例并注册进 `hardware.SPECS`(参考 `ascend950dt.py`),接口不变;默认规格恒为 `ASCEND950PR`。 3. **标定 T_cmd**: 当前按 0 处理 (未标定, issue#36; MergeBatch 合并收益已由 move_eff 搬移效率模型刻画, 不依赖 T_cmd 估计值), 实测后改 `t_cmd_ns` 一处即可。 4. **新分支**: 在 `branches/` 下加一个文件实现三接口 + 在 `router.py` 注册 + 在 `evaluator._BRANCH_EVAL` 注册;约束一律走 `constraints.py`,不在分支内另造规则。 diff --git a/BMM/BMM_Theory/tests/test_branches.py b/BMM/BMM_Theory/tests/test_branches.py index be2a723..0347424 100644 --- a/BMM/BMM_Theory/tests/test_branches.py +++ b/BMM/BMM_Theory/tests/test_branches.py @@ -1028,5 +1028,79 @@ class TestIssue37(unittest.TestCase): self.assertAlmostEqual(t.t_total, t.t_steady + t.t_drain) +class TestIssue38(unittest.TestCase): + """issue#38: 昇腾950 系列多 SKU 硬件规格 (950PR 32/28核 + 950DT 36/32/28核, + 白皮书表3-1/表4-2), 默认加载调用不变.""" + + def test_default_spec_unchanged(self): + # 默认加载调用不受影响: ASCEND950PR 各项与 NpuSpec() 关键字默认构造等价 + from bmm_theory.hardware import NpuSpec, get_spec + s = ASCEND950PR + self.assertEqual((s.aic_num, s.aiv_num), (32, 64)) + self.assertEqual(s.bw_gm, 1.6e12) + self.assertEqual(s.l2_bytes, 128 * 1024 * 1024) + self.assertAlmostEqual(s.q16, 486e12 / 32) + self.assertEqual(s.gm_capacity_gb, 128.0) + self.assertIs(get_spec(), ASCEND950PR) # 默认 = 950PR 主bin + self.assertIs(get_spec("Ascend950PR"), ASCEND950PR) + self.assertEqual(NpuSpec(), ASCEND950PR) # 无参构造 == 主bin + + def test_spec_registry_five_skus(self): + from bmm_theory.hardware import SPECS + self.assertEqual(sorted(SPECS), [ + "Ascend950DT", "Ascend950DT_C28", "Ascend950DT_C32", + "Ascend950PR", "Ascend950PR_C28"]) + for s in SPECS.values(): + # 共架构: 单核 Cube 口径全系列一致 (白皮书总算力行取整, 28核档 + # 425/28=15.179T 与 15.1875T 差 0.06%, 容差 0.1%); AIV 恒为 AIC 两倍 + self.assertLess(abs(s.q16 / (486e12 / 32) - 1), 1e-3) + self.assertEqual(s.aiv_num, 2 * s.aic_num) + self.assertEqual(s.l1_bytes, 512 * 1024) # 表4-2 各档一致 + self.assertEqual(s.l0c_bytes, 256 * 1024) + + def test_950dt_specs(self): + from bmm_theory.hardware import (ASCEND950DT, ASCEND950DT_C28, + ASCEND950DT_C32) + self.assertEqual((ASCEND950DT.aic_num, ASCEND950DT.aiv_num), (36, 72)) + self.assertEqual(ASCEND950DT.bw_gm, 4.0e12) # 4TB/s HBM + self.assertEqual(ASCEND950DT.l2_bytes, 128 * 1024 * 1024) + self.assertEqual(ASCEND950DT.gm_capacity_gb, 144.0) + self.assertEqual(ASCEND950DT.cube_peak_tflops, 547.0) + self.assertEqual((ASCEND950DT_C32.aic_num, + ASCEND950DT_C32.cube_peak_tflops), (32, 486.0)) + self.assertEqual((ASCEND950DT_C28.aic_num, + ASCEND950DT_C28.cube_peak_tflops), (28, 425.0)) + self.assertEqual(ASCEND950DT_C28.gm_capacity_gb, 96.0) + # 派生量: 单核 GM 份额 4TB/36; r16 平衡点随带宽升至 4TB/s 而减半 + self.assertAlmostEqual(ASCEND950DT.bw_pc, 4.0e12 / 36) + self.assertAlmostEqual(ASCEND950DT.r16, 547e12 / (4.0e12 / 2)) + self.assertAlmostEqual(ASCEND950DT_C32.r16, 486e12 / (4.0e12 / 2)) + # AIV 白皮书交叉验证: fp32 通量 72 核 x 128 lane x 1.65GHz x 2 ≈ 30T + self.assertAlmostEqual(ASCEND950DT.aiv_elem_rate_fp32 * 2 / 1e12, + 30.0, places=0) + + def test_950pr_c28(self): + from bmm_theory.hardware import ASCEND950PR_C28 + self.assertEqual((ASCEND950PR_C28.aic_num, + ASCEND950PR_C28.aiv_num), (28, 56)) + self.assertEqual(ASCEND950PR_C28.bw_gm, 1.4e12) + self.assertEqual(ASCEND950PR_C28.l2_bytes, 112 * 1024 * 1024) + self.assertEqual(ASCEND950PR_C28.gm_capacity_gb, 112.0) + + def test_get_spec_unknown_raises(self): + from bmm_theory.hardware import get_spec + with self.assertRaises(KeyError): + get_spec("Ascend910") + + def test_dt_router_smoke(self): + # 换芯片只换 spec: DT 路由/时延正常; 同一 case GM 段 4TB/s 快于 1.6TB/s + from bmm_theory.hardware import ASCEND950DT + case = mkcase(8, 4096, 4096, 1024) + r_dt = BranchRouter(ASCEND950DT).route(case) + r_pr = BranchRouter().route(case) + self.assertGreater(r_dt["timing"].t_total, 0.0) + self.assertLess(r_dt["timing"].t_mte2_gm, r_pr["timing"].t_mte2_gm) + + if __name__ == "__main__": unittest.main()