Fix #35: MergeBatch L1绑定情形 DMA 命令数多计 b0 倍修复 + 分界泛化口径
- evaluate: 每核命令数 = ceil(b_core/b0) * ceil(K/k_l1^m) (K截断退化为 b_core/b0, 数值不变; L1绑定消除 b0 倍多计 —— v1.1 §4.4 恒劣恒等式的 n_K 是未合并粒度, 误代入合并后段数会多计 b0 倍, 可把仲裁方向翻错) - beats_iterbatch: 泛化为实际命令数比较 (cmds_iter=b_core*ceil(K/k_l1_iter) vs cmds_mb=ceil(b_core/b0)*ceil(K/k_l1^m), 节省>T_cmd vs drain 惩罚); K截断时严格 退化为文档闭式 b_core > b0*(T_comp+T_write)/T_cmd; 截断判定改用合并口径 plan.k_l1>=K (未合并截断不代表合并后截断); 覆盖 dValue 512B cap 第三情形; T_cmd<=0 策略路径改为 cmds_mb<cmds_iter 判 MergeBatch 优先 - router: 仲裁文案 [裁决] 位打印最终胜者 (修复分界/时延不一致时的自相矛盾表述) - 用户 case 家族 B=128,M=1~16,N=128,K=512 修复后: m=1/2/4 -> MergeBatch, m=8/16 -> IterBatch (修复前全判 IterBatch; 交叉点 m≈4~8, 物理合理) - 测试: TestIssue35 回归 5 例 (命令数公式/K截断不变/口径一致/路由家族/裁决文案); test_beats_iterbatch_policy 的 (128,64,64,512) 期望 True->False (第三情形: 合并侧 dValue cap 截断, 命令数 4=4 打平, 恒劣 —— 原期望基于误分类) - docs/01_MergeBatch分支.md: 分界小节补第三情形行 + 命令数口径警示 + 泛化净收益式 - 验证: 68/68 unittest; examples 重生成可复现 0 diff (仅仲裁文案 + 16.0->16 格式, plans.csv 不变); 压力 10000 例 (seed7/6000+seed2024/4000): 0 崩溃/0 NaN/0 违规/ 0 不可行/0 GM<V_in, 七分支全覆盖
This commit is contained in:
@@ -7,9 +7,10 @@
|
||||
核心思想: 合并 b0 个 batch 的 A'[b0*M,K] @ B'[K,b0*N] 为单次 DMA 搬入,
|
||||
减少 GM->L1 搬移命令数 (省 b0 倍 T_cmd); 交叉项被算出但丢弃 (冗余比例 (b0-1)/b0),
|
||||
进入条件 5 保证 case 为访存 Bound, 冗余算力被搬移时延掩盖.
|
||||
T_cmd=0 时 (无命令固定时延/未标定) 命令时延收益不可量化, 但合并仍省 b0 倍
|
||||
T_cmd=0 时 (无命令固定时延/未标定) 命令时延收益不可量化, 但合并仍可减少
|
||||
搬移命令/主机指令数 (指令发射/调度/同步收益未建模) —— beats_iterbatch 按既定
|
||||
策略裁决: K截断即优先 MergeBatch (模型内代价仅 drain 惩罚), L1 绑定恒劣.
|
||||
策略裁决: 合并后实际每核命令数更少即优先 MergeBatch (模型内代价仅 drain 惩罚),
|
||||
命令数打平恒劣 (issue#35 泛化口径, 覆盖 dValue 512B cap 截断等第三情形).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -194,7 +195,7 @@ class MergeBatchBranch(Branch):
|
||||
out_l2 = output_to_l2(case, s, 0.0)
|
||||
w_fix = s.bw_l2_pc if out_l2 else s.bw_pc
|
||||
|
||||
k_truncated = k_l1 >= k
|
||||
k_truncated = k_l1 >= k # 保留语义标记 (plan.note/调试用)
|
||||
# 每 K 分块计算时延 (未合并基准, v1.1 §4.1 符号) / 单 batch 输出写回 (R4)
|
||||
t_comp_chunk = 2.0 * m * n * k_l1 / qc
|
||||
t_write = m * n * out_b / w_fix
|
||||
@@ -202,14 +203,14 @@ class MergeBatchBranch(Branch):
|
||||
# 搬移 (issue#31): 合并组/切 K 各 (组, K段) 数据互不重叠, 每个输入字节恰好
|
||||
# 从 GM 读一次 (K截断与 L1 绑定均如此; 切 K 末段按实际剩余计, 不再整段上取)
|
||||
# -> GM 数据量 = V_in, 数据时延 = V_in/芯片带宽 (全核并发);
|
||||
# 搬移命令数只决定 T_cmd (与 IterBatch 的同/少 b0 倍关系不变):
|
||||
# K截断: 每核 b_core/b0 次合并搬入 (每次 b0 个 batch 全 K)
|
||||
# L1绑定: k_L1^m = k_L1/b0, n_K^m = b0*n_K, 命令数与 IterBatch 相同
|
||||
if k_truncated:
|
||||
dma_cmds = b_core / b0
|
||||
else:
|
||||
n_k = -(-k // k_l1)
|
||||
dma_cmds = b_core * n_k
|
||||
# 搬移命令数只决定 T_cmd (issue#35 修正: 真实命令数 = 合并组数 x 每组 K 段数,
|
||||
# 不得把"命令数与 IterBatch 相同 = b_core*n_K"(v1.1 §4.4, n_K 为未合并粒度)
|
||||
# 误代入合并后段数 —— 那样会多计 b0 倍):
|
||||
# K截断: n_K^m = 1 -> cmds = ceil(b_core/b0) (比 IterBatch 省 b0 倍)
|
||||
# L1绑定: k_L1^m = k_L1/b0 理想情形退化为 b_core*n_K, 与 IterBatch 相同;
|
||||
# dValue 512B cap 截断等情形按实际 ceil(K/k_l1^m) 计
|
||||
n_k = -(-k // k_l1)
|
||||
dma_cmds = -(-b_core // b0) * n_k
|
||||
t_dma_cmd = dma_cmds * s.t_cmd
|
||||
t_mte2_data = case.input_bytes / s.bw_gm
|
||||
t_mte2 = t_mte2_data + t_dma_cmd
|
||||
@@ -243,54 +244,83 @@ class MergeBatchBranch(Branch):
|
||||
def beats_iterbatch(self, case: BmmCase) -> tuple:
|
||||
"""返回 (MergeBatch是否更优, 说明).
|
||||
|
||||
MergeBatch 最优 ⟺ K截断 (k_L1=K) 且 b_core > b0*(T_comp+T_write)/T_cmd
|
||||
L1 绑定情形 MergeBatch 恒劣于 IterBatch (搬移次数相同, 只放大 drain).
|
||||
v1.1 §4.5 统一分界的泛化口径 (issue#35): 直接比较两分支**实际每核 DMA
|
||||
命令数**与 drain 惩罚 ——
|
||||
cmds_iter = b_core * ⌈K/k_l1_iter⌉ (IterBatch 逐 batch 逐 K 段一条)
|
||||
cmds_mb = ⌈b_core/b0⌉ * ⌈K/k_l1^m⌉ (合并组数 x 每组 K 段数)
|
||||
搬移节省 = (cmds_iter - cmds_mb) * T_cmd
|
||||
drain 惩罚 = (b0-1) * (T_comp + T_write) (T_comp 按未合并基线分块)
|
||||
MergeBatch 最优 ⟺ 搬移节省 > drain 惩罚.
|
||||
|
||||
T_cmd=0 (无命令时延/未标定) 时阈值趋于 +inf, 但 MergeBatch 还有**未量化的
|
||||
结构性收益**: 大 B 小 MN 时搬移命令数/主机指令数省 b0 倍 (每条命令的指令
|
||||
发射/调度/同步开销未建模). 因此 T_cmd<=0 采用既定策略: K截断即可胜
|
||||
(模型内代价仅为 drain 惩罚, 访存 Bound case 下小且方向已知); L1 绑定仍恒劣.
|
||||
与 v1.1 §4.5 闭式的等价性:
|
||||
- K 截断 (k_l1^m = K): cmds_mb = b_core/b0, 退化为文档闭式
|
||||
b_core > b0*(T_comp+T_write)/T_cmd;
|
||||
- L1 绑定理想情形 (k_l1^m = k_l1^iter/b0): 命令数相同, 节省=0 -> 恒劣;
|
||||
- dValue 512B 推荐值截断等第三情形 (文档二分未覆盖): 按实际命令数比较.
|
||||
|
||||
截断判定用**合并后** plan.k_l1 >= K (与 make_plan/evaluate 同源,
|
||||
issue#35): 未合并 k_l1 截断不代表合并后仍截断 (合并使 L1 占用放大 b0 倍,
|
||||
且受 dValue 512B 推荐值截断), v1.1 line 219 的字面定义 (未合并口径) 与
|
||||
line 161 的合并公式矛盾时以后者为准.
|
||||
|
||||
T_cmd<=0 (无命令时延/未标定) 时阈值不可量化: 按既定策略, 合并后每核命令数
|
||||
更少 (cmds_mb < cmds_iter, 结构性指令/调度收益未建模) 即判 MergeBatch 优先;
|
||||
命令数打平则恒劣 (合并只放大 drain).
|
||||
"""
|
||||
s = self.spec
|
||||
m, n, k = case.m, case.n, case.k
|
||||
dt = case.dtype_in_bytes
|
||||
out_b = case.dtype_out_bytes
|
||||
b_core = case.batch_c // s.aic_num
|
||||
|
||||
# IterBatch 基准的 k_L1 (未合并): L1 双缓冲单 batch
|
||||
k_l1_iter = min(k, s.l1_bytes / (2 * (m + n) * dt))
|
||||
k_truncated = k_l1_iter >= k
|
||||
# IterBatch 基线的每核命令数 (与 iter_batch.evaluate 同源: 形态 a/b 时
|
||||
# k_l1=K 一次一条; c/d 形态按 l1_form 判定的 k_l1 分段)
|
||||
from .iter_batch import IterBatchBranch
|
||||
_, k_l1_iter, _ = IterBatchBranch(s).l1_form(case)
|
||||
if not k_l1_iter:
|
||||
k_l1_iter = k
|
||||
n_k_iter = -(-k // min(k_l1_iter, k))
|
||||
cmds_iter = b_core * n_k_iter
|
||||
|
||||
# MergeBatch 实际每核命令数 (issue#35: 合并组数 x 每组 K 段数)
|
||||
plan = self.make_plan(case)
|
||||
b0 = plan.merge_b0
|
||||
# T_comp 按输入 dtype 算力 (issue#28); T_write 保持 v1.1 直写 GM 语义
|
||||
n_k_mb = -(-k // min(plan.k_l1, k))
|
||||
cmds_mb = -(-b_core // b0) * n_k_mb
|
||||
k_truncated = plan.k_l1 >= k # 合并口径截断判定 (issue#35)
|
||||
regime = f"K截断(k_l1^m={plan.k_l1}>=K)" if k_truncated else \
|
||||
f"L1绑定(k_l1^m={plan.k_l1}<K={k})"
|
||||
|
||||
# T_comp 按输入 dtype 算力 (issue#28), 未合并基线分块 (v1.1 §4.1 符号);
|
||||
# T_write 保持 v1.1 直写 GM 语义
|
||||
qc = s.q_cube(case.dtype_a, case.dtype_b)
|
||||
t_comp = 2.0 * m * n * min(k_l1_iter, k) / qc
|
||||
t_write = m * n * out_b / s.bw_pc
|
||||
drain_pen = (b0 - 1) * (t_comp + t_write)
|
||||
save_cmds = cmds_iter - cmds_mb
|
||||
|
||||
if s.t_cmd <= 0:
|
||||
# T_cmd=0: 命令时延收益不可量化 -> 阈值 +inf; 按策略裁决 (见 docstring).
|
||||
# 大 B 小 MN 时合并把 b_core 条搬移/计算命令序列并为 b_core/b0 条,
|
||||
# 指令发射/调度/同步收益存在但未量化 —— K截断时判胜, 由路由层以策略覆盖
|
||||
# 时延模型比较; L1 绑定 (搬移次数与 IterBatch 相同) 仍恒劣.
|
||||
if k_truncated:
|
||||
detail = (f"k_L1=K(截断); T_cmd=0: 命令时延收益不可量化(阈值=+inf), "
|
||||
f"但合并省 {b0} 倍搬移命令/指令数 (结构性收益, 未量化) -> "
|
||||
f"策略优先 MergeBatch; 模型内代价 drain 惩罚="
|
||||
f"{drain_pen*1e6:.2f}us")
|
||||
# T_cmd=0: 命令时延收益不可量化 -> 按结构性命令数比较的策略裁决
|
||||
# (合并把搬移/计算命令序列并少, 指令发射/调度/同步收益存在但未量化);
|
||||
# 命令数打平时合并只放大 drain -> 恒劣.
|
||||
if cmds_mb < cmds_iter:
|
||||
detail = (f"{regime}; T_cmd=0: 命令时延收益不可量化(阈值=+inf), "
|
||||
f"合并后每核命令数 {cmds_mb} < IterBatch {cmds_iter} "
|
||||
f"(结构性收益, 未量化) -> 策略优先 MergeBatch; "
|
||||
f"模型内代价 drain 惩罚={drain_pen*1e6:.2f}us")
|
||||
return True, detail
|
||||
detail = (f"k_L1={k_l1_iter:.0f}<K(L1绑定); T_cmd=0: 搬移命令次数与 "
|
||||
f"IterBatch 相同, 合并只放大 drain 惩罚={drain_pen*1e6:.2f}us -> 恒劣")
|
||||
detail = (f"{regime}; T_cmd=0: 每核命令数 MergeBatch={cmds_mb} 不少于 "
|
||||
f"IterBatch={cmds_iter}, 合并只放大 drain 惩罚="
|
||||
f"{drain_pen*1e6:.2f}us -> 恒劣")
|
||||
return False, detail
|
||||
|
||||
threshold = b0 * (t_comp + t_write) / s.t_cmd
|
||||
|
||||
win = k_truncated and (b_core > threshold)
|
||||
detail = (f"k_L1={'K(截断)' if k_truncated else f'{k_l1_iter:.0f}<K(L1绑定)'}; "
|
||||
f"b_core={b_core} vs 阈值 b0*(T_comp+T_write)/T_cmd={threshold:.1f}; "
|
||||
f"drain惩罚=(b0-1)*(T_comp+T_write)={drain_pen*1e6:.2f}us, "
|
||||
f"搬移节省=b_core*(1-1/b0)*T_cmd={b_core*(1-1/b0)*s.t_cmd*1e6:.2f}us")
|
||||
savings = save_cmds * s.t_cmd
|
||||
win = savings > drain_pen
|
||||
detail = (f"{regime}; 每核命令数 MergeBatch={cmds_mb} vs IterBatch={cmds_iter}, "
|
||||
f"搬移节省={savings*1e6:.2f}us vs drain惩罚=(b0-1)*(T_comp+T_write)="
|
||||
f"{drain_pen*1e6:.2f}us -> {'MergeBatch优' if win else 'IterBatch优'}")
|
||||
if k_truncated:
|
||||
detail += (f" (闭式等价: b_core={b_core} vs 阈值 "
|
||||
f"b0*(T_comp+T_write)/T_cmd={b0 * (t_comp + t_write) / s.t_cmd:.1f})")
|
||||
return win, detail
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -110,10 +110,10 @@ class BranchRouter:
|
||||
f"两分支均合法, 仲裁: "
|
||||
f"[分界条件] MergeBatch最优={mb_win} ({detail}); "
|
||||
f"[时延模型] T_MergeBatch={t_mb*1e6:.2f}us vs T_IterBatch={t_ib*1e6:.2f}us -> {lat_win}更优; "
|
||||
f"[裁决] {win}" + ("" if win == lat_win else f" (分界条件与时延模型不一致, 以时延模型为准: {lat_win})")
|
||||
f"[裁决] {lat_win}" + ("" if win == lat_win else
|
||||
f" (分界条件判{win}, 与时延模型不一致, 以时延模型为准)")
|
||||
)
|
||||
if win != lat_win:
|
||||
win = lat_win # 时延模型为最终裁决
|
||||
win = lat_win # 时延模型为最终裁决
|
||||
elif any(capable.values()):
|
||||
win = next(n for n, v in capable.items() if v)
|
||||
arbitration = f"仅 {win} 条件满足"
|
||||
|
||||
Reference in New Issue
Block a user