From f9d5da0c0cd2ff3e86d108e03d6aee2b878aeb57 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 9 Sep 2026 10:11:22 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20#37:=20ASW=5FBasic=20=E5=B0=BE=E8=BD=AE?= =?UTF-8?q?=E6=AE=8B=E4=BD=99=20drain=20=E9=97=AD=E5=BC=8F=E5=8C=96=20(?= =?UTF-8?q?=E6=96=B9=E6=A1=88=E7=94=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - P1 (bug, evaluate 路径): A0+r>0 的 t_drain 由"全 case 三级 max" (t_total≈2x 稳态) 修为块级残余 (1-ρ)·T_block (v1.5 §3.3) - P2: 周长型主导 + r>0 补尾轮残余: A1b (√ρ−ρ)·T_load; 方案B (√(n_wave(n_wave−1+ρ))−(n_wave−1+ρ))·T_load (v1.5 §5); 面积型与 r=0 残余恒 0 (§4.3 严格相等), recommend 稳态主口径不变 - 块级三段时延抽取 _block_times, _decide_tail 与 evaluate 同源 - P3/P4 仅文档标注 (docs/02_分支理论/06 §3): 首块填充/末块排空 (UnitFlag 16-granule 级小量, 量级未标定) 与尾轮重切流量放大 (√g / 1/√ρ) 不入模型 - tests: TestIssue37 七例锁数值 (81/81 通过); examples 44 行 0 diff; 压力回归 seed7/6000+seed2024/4000 干净 (0 崩溃/NaN/违规/GM tuple: + """单块三段时延 (尾轮文档 v1.5 §2.1 口径: k_L1 稳态约掉, L2 命中带宽). + + 返回 (t_mmad_blk, t_mte2_blk, t_fix_blk); _decide_tail 主导项判定与 + evaluate 尾轮残余 drain (issue#37) 同源共用. + """ + s = self.spec + qc = s.q_cube(case.dtype_a, case.dtype_b) # issue#28 + t_mmad = 2 * sm * sn * case.k / qc + t_mte2 = case.k * (sm + sn) * case.dtype_in_bytes / s.bw_l2_pc + t_fix = sm * sn * case.dtype_out_bytes / s.bw_pc + return t_mmad, t_mte2, t_fix + # ------------------------------------------------------------------ def _decide_tail(self, case, sm, sn, n_blk, k_l1) -> dict: """尾轮策略决策 (v1.5 闭式流程). 默认方案 B, 周长型且 rho>=rho_dv 时 A1b.""" @@ -391,7 +405,6 @@ class AswBasicBranch(Branch): r = n_blk % c rho = r / c dt = case.dtype_in_bytes - out_b = case.dtype_out_bytes base = dict(r=r, n_wave=n_wave, tail_m_cnt=1, tail_n_cnt=1, tail_m_main=0, tail_n_main=0) @@ -399,12 +412,8 @@ class AswBasicBranch(Branch): if r == 0: return {**base, "strategy": "A0", "reason": "r=0 无尾轮"} - # 主导项判定 - bw_eff = s.bw_l2_pc # L2 命中 - qc = s.q_cube(case.dtype_a, case.dtype_b) # issue#28 - t_mmad = 2 * sm * sn * case.k / qc - t_mte2 = case.k * (sm + sn) * dt / bw_eff - t_fix = sm * sn * out_b / s.bw_pc + # 主导项判定 (块级三段, 与 evaluate drain 同源 _block_times) + t_mmad, t_mte2, t_fix = self._block_times(case, sm, sn) t_block = max(t_mmad, t_mte2, t_fix) area_dominated = t_block != t_mte2 # 面积型 = MMAD 或 FIX 主导 @@ -455,7 +464,9 @@ class AswBasicBranch(Branch): - 输出落点 R4 (issue#30): 仅 S_A (整 case 输入+输出 <= L2) 驻留 L2 (5.2 写口, GM 写 = 0); S_B/S_C 直写 GM —— GM 读写共享总线累加由 assemble 的 MTE2 链处理 (issue#23); - - 字节列整芯片口径 (issue#29); Cube 算力按输入 dtype (issue#28). + - 字节列整芯片口径 (issue#29); Cube 算力按输入 dtype (issue#28); + - t_drain = 尾轮残余闭式 (issue#37, v1.5 §3.3/§4.3/§5): A0 (1-ρ)·T_block; + 周长型 A1b/方案B 残余见下; 面积型与 r=0 恒 0 (与稳态严格相等). """ s = self.spec b = case.batch_c @@ -484,10 +495,34 @@ class AswBasicBranch(Branch): # ---- Fixpipe (R4) ---- t_fix = out_all / (used * (s.bw_l2_pc if to_l2 else s.bw_pc)) - # drain: 尾轮暴露 (方案 B 已均匀重切, drain 小; A1b 尾轮凑满, drain 小; A0 尾轮 r 核空转) + # ---- drain: 尾轮残余时延 (issue#37, 尾轮 v1.5 §3.3/§4.3/§5 闭式) ---- + # 稳态聚合 t_steady ≡ (n_wave-1+rho)·T_block (均匀分块下 N_blk/used 恒等), + # drain 只计尾轮结构相对该稳态的残余: + # A0 (r>0): (1-rho)·T_block (T_A0 = n_wave·T_block) + # 面积型 A1b/方案B: 0 (§4.3 总量守恒, 与稳态严格相等) + # 周长型 A1b: (sqrt(rho)-rho)·T_load (§5: (n_wave-1+√ρ)·T_load) + # 周长型 方案B: (√(n_wave(n_wave-1+rho))-(n_wave-1+rho))·T_load + # 块级时延与 _decide_tail 同源 (_block_times, L2 命中口径); 未建模策略 + # (如 A1a) 维持原口径 0。首块填充/末块排空 (v1.91 §3.1 O(T_comp+T_write)) + # 在 UnitFlag 16-granule 细粒度流水下为 granule 级小量, 量级未标定, + # 不入模型 (docs/02_分支理论/06 §3 标注)。 t_drain = 0.0 - if plan.tail_strategy == "A0" and plan.tail_block_cnt > 0: - t_drain = max(t_mmad, t_gm + t_l2, t_fix) # 尾轮空转一个整块 + r = plan.tail_block_cnt + if r > 0: + n_wave = plan.tail_wave_num if plan.tail_wave_num > 0 \ + else ceil_div(b * m_cnt * n_cnt, used) + rho = min(r / used, 1.0) + t_mm_b, t_mv_b, t_fx_b = self._block_times( + case, plan.single_core_m, plan.single_core_n) + t_block = max(t_mm_b, t_mv_b, t_fx_b) + if plan.tail_strategy == "A0": + t_drain = (1.0 - rho) * t_block + elif t_block == t_mv_b: # 仅周长型主导有残余 (面积型严格 0) + if plan.tail_strategy == "A1b": + t_drain = (math.sqrt(rho) - rho) * t_mv_b + elif plan.tail_strategy == "方案B": + t_drain = (math.sqrt(n_wave * (n_wave - 1 + rho)) + - (n_wave - 1 + rho)) * t_mv_b return assemble_timing( t_mte2_gm=t_gm, t_mte2_l2=t_l2, t_dma_cmd=0.0, diff --git a/BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md b/BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md index 8dad11f..20029ee 100644 --- a/BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md +++ b/BMM/BMM_Theory/docs/02_分支理论/06_ASW_Basic分支.md @@ -45,6 +45,27 @@ GM = V_in 一次(r_in=1),共享块重复读全部命中 L2;S_C 双侧均 全命中 L2)。**分配策略的全部目标就是让 r_in 尽量接近 1**:S_B 已识别"单侧可全驻留" 调度(不再把可驻留 case 错误地按分组放大 GM);只有双侧都放不下 L2 时(S_C)r_in 才 > 1。 +**尾轮残余 T_drain(闭式, issue#37)**:均匀分块下稳态聚合 ≡ `(n_wave−1+ρ)·T_block` +(`N_blk/used = n_wave−1+ρ` 恒等),`T_drain` 只计尾轮结构相对稳态的残余;块级三段 +时延与 `_decide_tail` 主导项判定同源(L2 命中口径): + +| 策略 / 主导项 | T_drain | 出处 | +|---|---|---| +| r = 0 | 0 | — | +| A0(r>0,尾轮 r 核各 1 整块、C−r 核空转) | `(1−ρ)·T_block` | v1.5 §3.3 `T_A0 = n_wave·T_block` | +| 面积型 A1b / 方案B | 0 | v1.5 §4.3 总量守恒严格相等 | +| 周长型 A1b | `(√ρ−ρ)·T_load` | v1.5 §5 `T = (n_wave−1+√ρ)·T_load` | +| 周长型 方案B | `(√(n_wave(n_wave−1+ρ)) − (n_wave−1+ρ))·T_load` | v1.5 §5 `T = √(n_wave(n_wave−1+ρ))·T_load` | + +边界说明(**不计入模型**的两项, issue#37 决议仅标注): + +- **首块填充/末块排空**:v1.91 §3.1 的 `T_drain = O(T_comp+T_write)` 在 UnitFlag + 16×16×16 细粒度流水下真实暴露为 granule 级小量(首 k 段搬入 + 末 granule 排空), + 量级未标定,不入模型——大 n_wave 时相对误差 O(1/n_wave); +- **尾轮重切的流量放大**:方案B(全局 tile 缩 1/√g)与 A1b(尾轮区缩 √ρ)会真实 + 增加搬移总量(周长和 ×√g / ×1/√ρ),字节列仍按主 tile 几何计账;仅影响周长型 + 主导角区的精度(面积型 MTE2 非瓶颈,无影响)。 + ## 4. 核间分配策略:B→M→N 线性映射 **结论:B 优先分组在任何场景下都不优于线性映射**,根本原因: diff --git a/BMM/BMM_Theory/tests/test_branches.py b/BMM/BMM_Theory/tests/test_branches.py index d5ad914..be2a723 100644 --- a/BMM/BMM_Theory/tests/test_branches.py +++ b/BMM/BMM_Theory/tests/test_branches.py @@ -931,5 +931,102 @@ class TestIssue36(unittest.TestCase): self.assertEqual(mb.timing.gm_read_bytes, ib.timing.gm_read_bytes) +class TestIssue37(unittest.TestCase): + """issue#37: ASW_Basic evaluate 尾轮残余 drain 闭式化 (方案甲). + + - P1: A0 + r>0 的 drain 由"全 case 三级 max"(≈2x 稳态) 修为 (1-ρ)·T_block; + - P2: 周长型 A1b/方案B 补残余 (√ρ−ρ) / (√(n_wave(n_wave−1+ρ))−(n_wave−1+ρ))·T_load; + - 面积型 A1b/方案B 与 r=0 残余恒 0 (v1.5 §4.3 严格相等); + - 块级三段时延与 _decide_tail 同源 (_block_times, L2 命中口径). + """ + + def setUp(self): + from bmm_theory.branches.asw_basic import AswBasicBranch + self.br = AswBasicBranch() + self.s = ASCEND950PR + + @staticmethod + def _plan(case, sm, sn, m_cnt, n_cnt, strategy, r, n_wave): + from bmm_theory.models import ImplPlan + return ImplPlan(case_id=case.case_id, branch="ASW_Basic", + used_core_num=32, m_cnt=m_cnt, n_cnt=n_cnt, + single_core_m=sm, single_core_n=sn, + single_core_k=case.k, k_l1=128, + base_m=256, base_n=256, base_k=64, + tail_strategy=strategy, tail_block_cnt=r, + tail_wave_num=n_wave, fixpipe_unitflag=True, + out_dtype_bytes=case.dtype_out_bytes) + + def test_block_times_formula(self): + # 块级三段 = v1.5 §2.1 口径 (k_L1 约掉, L2 命中带宽) + case = mkcase(3, 1024, 1024, 1024) + t_mm, t_mv, t_fx = self.br._block_times(case, 256, 256) + self.assertAlmostEqual(t_mm, 2 * 256 * 256 * 1024 + / self.s.q_cube("bf16", "bf16")) + self.assertAlmostEqual(t_mv, 1024 * (256 + 256) * 2 / self.s.bw_l2_pc) + self.assertAlmostEqual(t_fx, 256 * 256 * 2 / self.s.bw_pc) + + def test_a0_drain_is_block_level_residual(self): + # P1 修复: A0 + r>0 的 drain = (1-ρ)·T_block (修复前误用全 case 三级 + # max -> t_total ≈ 2x 稳态) + case = mkcase(3, 1024, 1024, 1024) + p = self._plan(case, 512, 128, 2, 8, "A0", r=16, n_wave=2) + t = self.br.evaluate(case, p) + t_block = max(self.br._block_times(case, 512, 128)) + self.assertAlmostEqual(t.t_drain, (1 - 16 / 32) * t_block) + self.assertAlmostEqual(t.t_total, t.t_steady + t.t_drain) + self.assertLess(t.t_drain, t.t_steady) # 不再 ~2x 稳态 + + def test_area_dominated_tail_residual_zero(self): + # 面积型 (MMAD/FIX 主导): A1b/方案B 残余恒 0 (v1.5 §4.3 严格相等) + case = mkcase(3, 1024, 1024, 1024) + for strat in ("A1b", "方案B"): + p = self._plan(case, 512, 128, 2, 8, strat, r=16, n_wave=2) + t = self.br.evaluate(case, p) + self.assertEqual(t.t_drain, 0.0, strat) + self.assertAlmostEqual(t.t_total, t.t_steady) + + def test_perimeter_a1b_residual(self): + # P2: 周长型 (块级 MTE2 主导) + A1b: drain = (√ρ−ρ)·T_load + case = mkcase(1, 640, 1408, 4096) + t_mm, t_mv, t_fx = self.br._block_times(case, 64, 64) + self.assertGreater(t_mv, max(t_mm, t_fx)) # 确认为周长型前提 + p = self._plan(case, 64, 64, 10, 22, "A1b", r=28, n_wave=7) + t = self.br.evaluate(case, p) + rho = 28 / 32 + self.assertAlmostEqual(t.t_drain, (rho ** 0.5 - rho) * t_mv) + self.assertAlmostEqual(t.t_total, t.t_steady + t.t_drain) + + def test_perimeter_planb_residual(self): + # P2: 周长型 + 方案B: drain = (√(n_wave(n_wave−1+ρ))−(n_wave−1+ρ))·T_load + case = mkcase(1, 640, 1408, 4096) + p = self._plan(case, 64, 64, 10, 22, "方案B", r=28, n_wave=7) + t = self.br.evaluate(case, p) + t_mv = self.br._block_times(case, 64, 64)[1] + rho, x = 28 / 32, 7 - 1 + 28 / 32 + self.assertAlmostEqual(t.t_drain, ((7 * x) ** 0.5 - x) * t_mv) + self.assertGreater(t.t_drain, 0.0) + + def test_r0_drain_zero(self): + case = mkcase(3, 1024, 1024, 1024) + p = self._plan(case, 512, 128, 2, 8, "A0", r=0, n_wave=2) + self.assertEqual(self.br.evaluate(case, p).t_drain, 0.0) + + def test_make_plan_perimeter_a1b_end_to_end(self): + # 端到端 (make_plan 自产方案): 瘦长 case 周长型 + ρ≥ρ_dv -> A1b, + # drain 与闭式一致 (_decide_tail 与 evaluate 同源) + case = mkcase(33, 16, 8192, 7168) + p = self.br.make_plan(case) + self.assertGreater(p.tail_block_cnt, 0) + self.assertEqual(p.tail_strategy, "A1b", p.note) + t_mm, t_mv, t_fx = self.br._block_times( + case, p.single_core_m, p.single_core_n) + self.assertGreater(t_mv, max(t_mm, t_fx)) # 周长型前提 + t = self.br.evaluate(case, p) + rho = p.tail_block_cnt / 32 + self.assertAlmostEqual(t.t_drain, (rho ** 0.5 - rho) * t_mv) + self.assertAlmostEqual(t.t_total, t.t_steady + t.t_drain) + + if __name__ == "__main__": unittest.main()