Fix review issues #11-#16: StreamK fixpipe 单次计账 / K=1 AIV单缓冲方案 / MergeBatch b0 L0A/L0B 上限+路由可行回退 / advice-StreamK / 输入校验 / .gitignore+死代码清理
This commit is contained in:
@@ -94,10 +94,14 @@ class MergeBatchBranch(Branch):
|
||||
b = case.batch_c
|
||||
b_core = b // s.aic_num
|
||||
|
||||
# Step 1: 合并数 b0 (L0C + 算存比双上限, 尽量取 b_core 的因子)
|
||||
# Step 1: 合并数 b0 (L0C + L0A/L0B + 算存比 + b_core 四类上限, 尽量取 b_core 的因子)
|
||||
# L0A/L0B 上限 (issue#13): 合并 tile = (b0*M)x(b0*N), base_k 有 16 (fractal) 硬底,
|
||||
# 须满足 b0*M*16*dt*2 <= L0A 且 b0*N*16*dt*2 <= L0B, 否则合并后 L0 tile 无法驻留.
|
||||
b0_l0c = math.sqrt(s.l0c_bytes / (2 * m * n * 4))
|
||||
b0_ai = s.r16 * (m + n) / (2 * m * n)
|
||||
b0_max = int(min(b0_l0c, b0_ai, b_core))
|
||||
b0_l0a = s.l0a_bytes / (2 * m * s.fractal * dt)
|
||||
b0_l0b = s.l0b_bytes / (2 * n * s.fractal * dt)
|
||||
b0_max = int(min(b0_l0c, b0_ai, b0_l0a, b0_l0b, b_core))
|
||||
b0 = max(MIN_B0, self._factor_floor(b_core, b0_max))
|
||||
|
||||
# Step 2: L0 级 K 粒度 k_L0
|
||||
|
||||
@@ -24,11 +24,14 @@ class SpecialBranch(Branch):
|
||||
c1 = case.k <= 1
|
||||
checks = [ConditionCheck("1_K<=1 (Cube 无用)", c1, f"K={case.k}")]
|
||||
if case.k == 1:
|
||||
# K=1 触发 AIV 通路需 B >= 2*AIV核数 且单 batch 输入输出能驻留 UB
|
||||
c2 = case.batch_c >= 2 * self.spec.aiv_num
|
||||
# K=1 的 AIV 通路恒可用 (issue#12): B>=2*AIV 开 UB 乒乓; B<128 退化为
|
||||
# AIV 单缓冲 (无乒乓, 逐 batch 串行搬入), 不再是无方案空洞.
|
||||
b = case.batch_c
|
||||
pingpong = b >= 2 * self.spec.aiv_num
|
||||
mode = "UB乒乓" if pingpong else "AIV单缓冲(逐batch串行, B<2*AIV)"
|
||||
checks.append(ConditionCheck(
|
||||
"2_K=1的AIV触发: B >= 2*AIV核数 (开UB乒乓)",
|
||||
c2, f"B={case.batch_c} vs {2*self.spec.aiv_num}"))
|
||||
"2_K=1的AIV通路: 恒可用 (B>=128 开UB乒乓, 否则单缓冲)",
|
||||
True, f"B={b}, 模式={mode}"))
|
||||
return checks
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -36,11 +39,15 @@ class SpecialBranch(Branch):
|
||||
s = self.spec
|
||||
if case.k == 0:
|
||||
sub = "K=0纯写值"
|
||||
mode = ""
|
||||
note = "无任何计算, C=bias 或 0, 纯 AIV 写值; 按行均分到 AIV 核"
|
||||
else:
|
||||
sub = "K=1逐元素乘"
|
||||
note = ("退化为 C=A⊙B 无累加深度, Cube 16x16x16 粒度浪费 15/16; "
|
||||
"走 AIV 通路 GM->UB->Mul->GM, UB 乒乓")
|
||||
pingpong = case.batch_c >= 2 * s.aiv_num
|
||||
mode = "UB乒乓" if pingpong else "AIV单缓冲"
|
||||
note = (f"退化为 C=A⊙B 无累加深度, Cube 16x16x16 粒度浪费 15/16; "
|
||||
f"走 AIV 通路 GM->UB->Mul->GM, {mode} "
|
||||
f"({'B>=2*AIV 双batch乒乓流水' if pingpong else 'B<2*AIV 逐batch单缓冲串行'})")
|
||||
return ImplPlan(
|
||||
case_id=case.case_id, branch=self.name,
|
||||
used_core_num=s.aiv_num, # 用 AIV 核
|
||||
@@ -48,7 +55,8 @@ class SpecialBranch(Branch):
|
||||
core_map="AIV 核间按行均分 (无 Cube tile 概念)",
|
||||
b_core=0, merge_b0=1,
|
||||
single_core_m=0, single_core_n=0, single_core_k=case.k,
|
||||
k_l1=0, b_l1=1, l1_form="UB驻留(AIV)",
|
||||
k_l1=0, b_l1=1,
|
||||
l1_form="UB驻留(AIV)" if case.k == 0 else "UB驻留(AIV) " + mode,
|
||||
base_m=0, base_n=0, base_k=0,
|
||||
l2_policy_in="allocate", l2_policy_out="direct_gm",
|
||||
swizzle_w=0, workspace_bytes=0,
|
||||
|
||||
@@ -145,13 +145,14 @@ class StreamKBranch(Branch):
|
||||
t_mmad = t_mmad_tile / grid_k
|
||||
t_mte2 = t_mte2_tile / grid_k
|
||||
|
||||
# 归约: 部分和 4B 驻留 L2, AIV 归约 (含最终按 C dtype 写回)
|
||||
# 归约: 部分和 4B 驻留 L2, AIV 归约 (含部分和写/读回/求和/最终按 C dtype 写回)
|
||||
# 口径 (issue#11): 归约整体为串行追加 (t_drain=t_reduce, reduce_serial=True),
|
||||
# 部分和写出已计入 eval_streamk_reduce 的 t_write_partial —— 稳态 Fixpipe 不再
|
||||
# 重复计账. 此前按 grid_k*tile*4B/单核带宽份额另计一次, 既重复计账又把整组
|
||||
# 部分和串行压到单核写口, 高估 grid_k 倍 (streamk_demo 曾虚高到 55us/FIXPIPE).
|
||||
t_reduce = eval_streamk_reduce(tile_elems, grid_k, out_b, s)
|
||||
|
||||
# Fixpipe: 部分和写出按 4B (L0C dtype, 防精度丢失), 驻留 L2.
|
||||
# 最终归约结果的 C dtype 写回已在 t_reduce 内计, 此处不重复 (issue#9 口径对齐).
|
||||
fix_bytes = grid_k * tile_elems * 4
|
||||
t_fix = fix_bytes / s.bw_l2_pc
|
||||
fix_bytes = 0.0
|
||||
t_fix = 0.0
|
||||
|
||||
flops_pc = 2.0 * tile_elems * k / grid_k
|
||||
gm_bytes = k * (2 * math.sqrt(tile_elems)) * dt / grid_k
|
||||
|
||||
@@ -75,8 +75,18 @@ class PlanEvaluator:
|
||||
tips.append("瓶颈在 Cube 计算: 已接近理论算力上限, 检查是否有冗余计算 "
|
||||
"(MergeBatch 交叉项) 可消除")
|
||||
elif bn == "FIXPIPE":
|
||||
tips.append("瓶颈在 Fixpipe 写出: 检查输出 dtype (fp16/fp8 可减半写出量), "
|
||||
"或评估输出驻留 L2 异步回写策略")
|
||||
if plan.branch == "StreamK":
|
||||
# StreamK 部分和按 L0C dtype 4B 防精度丢失, 不随 C 的 fp16/fp8 转换,
|
||||
# dtype 减半提示不适用 (issue#14); 写账已并入归约, 需查归约侧配置
|
||||
tips.append("瓶颈标注在 Fixpipe: StreamK 的部分和写出已并入归约计账 "
|
||||
"(4B 防精度丢失, 不可随 C dtype 减半), 请核查 L2 写口/"
|
||||
"归约并行度(grid_K) 设置")
|
||||
else:
|
||||
tips.append("瓶颈在 Fixpipe 写出: 检查输出 dtype (fp16/fp8 可减半写出量), "
|
||||
"或评估输出驻留 L2 异步回写策略")
|
||||
elif bn == "REDUCE":
|
||||
tips.append("瓶颈在 StreamK 归约 (串行追加): 可增大 grid_K 摊薄归约 "
|
||||
"或核对确定性要求是否允许 StreamK")
|
||||
if plan.branch == "MergeBatch" and plan.k_l1 < case.k:
|
||||
tips.append("警告: MergeBatch 处于 L1 绑定情形 (k_L1<K), 理论证明其恒劣于 "
|
||||
"IterBatch (v1.1 §4.4), 建议改用 IterBatch")
|
||||
|
||||
@@ -84,6 +84,26 @@ class BmmCase:
|
||||
deterministic_level: int = 0 # 确定性等级, >=2 禁用 StreamK
|
||||
|
||||
# ---- 派生属性 ----
|
||||
def __post_init__(self):
|
||||
"""输入合法性校验 (issue#15): 非法维度/负值静默产出伪方案, 必须明确报错."""
|
||||
bad = []
|
||||
for nm, v, lo, ok0 in (("batch_a", self.batch_a, 1, False),
|
||||
("batch_b", self.batch_b, 1, False),
|
||||
("m", self.m, 1, False),
|
||||
("n", self.n, 1, False),
|
||||
("k", self.k, 0, True)):
|
||||
if not isinstance(v, int):
|
||||
bad.append(f"{nm}={v!r} 非整数")
|
||||
elif v < lo or (v == 0 and not ok0):
|
||||
bad.append(f"{nm}={v} 非法 (需 >= {lo})")
|
||||
if bad:
|
||||
raise ValueError("case 维度非法: " + "; ".join(bad) +
|
||||
" (m/n/batch 必须为正, k 可为 0)")
|
||||
for nm, dt in (("dtype_a", self.dtype_a), ("dtype_b", self.dtype_b),
|
||||
("dtype_c", self.dtype_c)):
|
||||
if str(dt).strip().lower() not in DTYPE_BYTES:
|
||||
raise ValueError(f"不支持的 dtype: {dt!r}, 支持 {sorted(DTYPE_BYTES)}")
|
||||
|
||||
@property
|
||||
def batch_c(self) -> int:
|
||||
return max(self.batch_a, self.batch_b)
|
||||
|
||||
@@ -78,11 +78,9 @@ class BranchRouter:
|
||||
mb = self.merge_batch.analyze(case)
|
||||
ib = self.iter_batch.analyze(case)
|
||||
|
||||
candidates = []
|
||||
if mb.capable:
|
||||
candidates.append((self.merge_batch.name, mb))
|
||||
if ib.capable:
|
||||
candidates.append((self.iter_batch.name, ib))
|
||||
# 候选表: [(分支名, BranchResult)], 顺序 = 仲裁优先级
|
||||
cand_map = {self.merge_batch.name: mb, self.iter_batch.name: ib}
|
||||
capable = {n: r.capable for n, r in cand_map.items()}
|
||||
|
||||
arbitration = ""
|
||||
if mb.capable and ib.capable:
|
||||
@@ -100,8 +98,8 @@ class BranchRouter:
|
||||
)
|
||||
if win != lat_win:
|
||||
win = lat_win # 时延模型为最终裁决
|
||||
elif candidates:
|
||||
win = candidates[0][0]
|
||||
elif any(capable.values()):
|
||||
win = next(n for n, v in capable.items() if v)
|
||||
arbitration = f"仅 {win} 条件满足"
|
||||
else:
|
||||
# 切B分支都不满足, 尝试 StreamK 再回落 ASW
|
||||
@@ -115,11 +113,37 @@ class BranchRouter:
|
||||
f"IterBatch未过: {ib.failed_conditions()}; "
|
||||
f"MergeBatch未过: {mb.failed_conditions()}")
|
||||
|
||||
chosen = mb if win == self.merge_batch.name else ib
|
||||
# 可行性保障 (issue#13): 仲裁胜出方案必须通过约束自检, 否则按
|
||||
# (另一切B候选 -> StreamK -> ASW_Basic) 顺序回退到首个可行方案.
|
||||
from .constraints import check_plan_constraints
|
||||
|
||||
def _feasible(n):
|
||||
r = cand_map[n]
|
||||
return r.plan is not None and not check_plan_constraints(case, r.plan, self.spec)
|
||||
|
||||
if _feasible(win):
|
||||
chosen = cand_map[win]
|
||||
else:
|
||||
loser = self.merge_batch.name if win == self.iter_batch.name else self.iter_batch.name
|
||||
fallback_note = (f"; 但 {win} 方案自检违规: "
|
||||
f"{'; '.join(check_plan_constraints(case, cand_map[win].plan, self.spec))}")
|
||||
if capable.get(loser) and _feasible(loser):
|
||||
chosen, win = cand_map[loser], loser
|
||||
fallback_note += f", 回退可行候选 {loser}"
|
||||
else:
|
||||
sk = self.stream_k.analyze(case)
|
||||
if sk.capable and sk.plan is not None and \
|
||||
not check_plan_constraints(case, sk.plan, self.spec):
|
||||
return self._wrap_checked(case, sk, arbitration + fallback_note + ", 落 StreamK")
|
||||
asw = self.asw_basic.analyze(case)
|
||||
if asw.plan is not None and not check_plan_constraints(case, asw.plan, self.spec):
|
||||
return self._wrap_checked(case, asw, arbitration + fallback_note + ", 回落 ASW_Basic")
|
||||
chosen, win = cand_map[win], win # 无可行方案: 保留原裁决, 由自检标注
|
||||
arbitration += fallback_note
|
||||
|
||||
result = BranchResult(capable=True, plan=chosen.plan, timing=chosen.timing)
|
||||
return self._wrap_checked(case, result, arbitration,
|
||||
candidates={n: r.capable for n, r in
|
||||
[(self.merge_batch.name, mb), (self.iter_batch.name, ib)]})
|
||||
candidates=capable)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def _wrap_checked(self, case: BmmCase, result: BranchResult, note: str,
|
||||
@@ -155,15 +179,3 @@ class BranchRouter:
|
||||
return {"branch": branch, "plan": plan, "timing": None,
|
||||
"arbitration": "[无方案] " + note, "candidates": {},
|
||||
"self_check_violations": []}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@staticmethod
|
||||
def _wrap(result: BranchResult, note: str) -> dict:
|
||||
return {
|
||||
"branch": result.plan.branch if result.plan else "未知",
|
||||
"plan": result.plan,
|
||||
"timing": result.timing,
|
||||
"arbitration": note + (f" | {result.note}" if result.note else ""),
|
||||
"candidates": {},
|
||||
"self_check_violations": [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user