Fix review issues #17-#22: 恢复 #11/#12/#14 (StreamK fixpipe 单次计账/K=1 AIV单缓冲/advice) + 占位方案不可评估 + 转置 dValue 判据三处同源(form c 双缓冲适配修复) + 恢复 #13/#15 回归测试 + 清理临时 csv/.gitignore + 文档同步
This commit is contained in:
@@ -43,22 +43,33 @@ class IterBatchBranch(Branch):
|
||||
return "b", k, f"双batch乒乓: 2*(MK+KN)*dtype={2*single/1024:.0f}KB <= L1"
|
||||
|
||||
# c) 一侧驻留 + 对侧切 K, 预算按 b_core 分档
|
||||
# dValue 守卫与条件 4 / constraints 同源 (issue#19): 转置感知连续维判据
|
||||
# (dv_a=M*dt 当 A 转置, 否则 k_l1*dt; dv_b=N*dt 当 B 不转置, 否则 k_l1*dt).
|
||||
from ..models import dvalue_contig_dims
|
||||
l1_budget = s.l1_bytes / min(b_core, 2)
|
||||
for resident, side in ((m * k * dt, "A"), (k * n * dt, "B")):
|
||||
other = n * dt if side == "A" else m * dt
|
||||
if resident <= l1_budget:
|
||||
k_l1 = min(int((l1_budget - resident) / other / 2), k)
|
||||
k_l1 = align_down(max(k_l1, s.fractal), s.fractal)
|
||||
if k_l1 >= s.fractal and k_l1 * dt >= s.dvalue_min:
|
||||
dv_a, dv_b = dvalue_contig_dims(case, k_l1)
|
||||
# 双缓冲适配: 驻留侧 + 对侧 k_l1 段 x2 必须 <= 预算
|
||||
# (resident 恰占满预算时 k_l1 被抬到 16 会造成超预算的假方案)
|
||||
if (k_l1 >= s.fractal and
|
||||
resident + 2 * k_l1 * other <= l1_budget and
|
||||
dv_a >= s.dvalue_min and dv_b >= s.dvalue_min):
|
||||
return "c", k_l1, (
|
||||
f"一侧驻留({side})+对侧切K: {side}驻留{resident/1024:.0f}KB, "
|
||||
f"预算L1/{min(b_core,2)}, k_L1={k_l1}")
|
||||
f"预算L1/{min(b_core,2)}, k_L1={k_l1}, "
|
||||
f"dValueA={dv_a:.0f}B/dValueB={dv_b:.0f}B")
|
||||
# b_core>=2 时另一半 L1 预取下一 batch 驻留侧, 边界无气泡
|
||||
|
||||
# d) 两侧都切 K (兜底)
|
||||
k_l1 = align_down(int(s.l1_bytes / (2 * (m + n) * dt)), s.fractal)
|
||||
if k_l1 >= s.fractal and k_l1 * dt >= s.dvalue_min:
|
||||
return "d", k_l1, f"两侧都切K: k_L1={k_l1}, K段成对流水, batch边界天然无缝"
|
||||
dv_a, dv_b = dvalue_contig_dims(case, k_l1)
|
||||
if (k_l1 >= s.fractal and dv_a >= s.dvalue_min and dv_b >= s.dvalue_min):
|
||||
return "d", k_l1, (f"两侧都切K: k_L1={k_l1}, K段成对流水, batch边界天然无缝; "
|
||||
f"dValueA={dv_a:.0f}B/dValueB={dv_b:.0f}B")
|
||||
|
||||
return None, 0, "L1 四形态均不满足 (M/N 相对 L1 过大)"
|
||||
|
||||
@@ -90,24 +101,18 @@ class IterBatchBranch(Branch):
|
||||
form is not None, form_desc))
|
||||
|
||||
# 条件 4: 搬移效率下限 (c/d 切分后)
|
||||
# 转置影响 (参考 bmmv3): A 不转置时 K 向连续, dValue 判 K*dt;
|
||||
# A 转置时 M 向连续, dValue 判 M*dt;
|
||||
# B 不转置时 N 向连续, dValue 判 N*dt;
|
||||
# B 转置时 K 向连续, dValue 判 K*dt.
|
||||
# dValue 判定按转置调整连续维 (与 l1_form 守卫/constraints 同源, issue#19):
|
||||
# A 不转置: K 向连续 -> k_l1*dt; A 转置: M 向连续 -> M*dt;
|
||||
# B 不转置: N 向连续 -> N*dt; B 转置: K 向连续 -> k_l1*dt.
|
||||
from ..models import dvalue_contig_dims
|
||||
m, n, k = case.m, case.n, case.k
|
||||
dt = case.dtype_in_bytes
|
||||
if form in ("c", "d"):
|
||||
tile_ok = (k_l1 * m * dt >= s.min_tile_size) or (k_l1 * n * dt >= s.min_tile_size)
|
||||
# dValue 判定按转置调整连续维
|
||||
if case.trans_a:
|
||||
dv_a = m * dt # A 转置: M 向连续
|
||||
else:
|
||||
dv_a = k_l1 * dt # A 不转置: K 向连续 (切分后)
|
||||
if case.trans_b:
|
||||
dv_b = k_l1 * dt # B 转置: K 向连续 (切分后)
|
||||
else:
|
||||
dv_b = n * dt # B 不转置: N 向连续
|
||||
dv_ok = dv_a >= s.dvalue_min or dv_b >= s.dvalue_min # 至少一侧满足
|
||||
dv_a, dv_b = dvalue_contig_dims(case, k_l1)
|
||||
# 切 K 时 A/B 两侧分段各自搬移, 两侧连续维 dValue 均须 >= 下限 (与生成守卫/约束同源,
|
||||
# issue#19; 非转置时两侧同为 k_l1*dt, 等价于历史口径)
|
||||
dv_ok = dv_a >= s.dvalue_min and dv_b >= s.dvalue_min
|
||||
c4 = tile_ok and dv_ok
|
||||
checks.append(ConditionCheck(
|
||||
"4_搬移效率: 搬移分块>=min_TileSize 且 dValue>=128B (转置调整连续维)",
|
||||
|
||||
@@ -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/#17): 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,15 @@ 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/#17): 归约整体为串行追加 (t_drain=t_reduce, reduce_serial=True),
|
||||
# 部分和写出已计入 eval_streamk_reduce 的 t_write_partial —— 稳态 Fixpipe 不再
|
||||
# 重复计账. 若再按 grid_k*tile*4B/单核带宽份额另计一次, 既重复计账又把整组
|
||||
# 部分和串行压到单核写口, 高估 grid_k 倍 (streamk_demo 曾虚高到 55us/FIXPIPE;
|
||||
# 第三轮曾回退该修复, issue#17 恢复).
|
||||
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
|
||||
|
||||
@@ -36,6 +36,13 @@ def check_plan_constraints(case: BmmCase, plan: ImplPlan,
|
||||
s = spec
|
||||
v = []
|
||||
|
||||
# 占位/无效方案: used_core_num<1 说明没有真实方案 (如 router._no_plan 的占位),
|
||||
# recommend 侧 advice 已标注"[无方案]", evaluate 侧必须判不可行 (issue#18),
|
||||
# 不得当作可行方案给正常时延.
|
||||
if plan.used_core_num < 1:
|
||||
v.append("used_core_num=0: 占位/未生成方案, 不可评估")
|
||||
return v
|
||||
|
||||
# AIV 通路: 只校验 AIV 核数
|
||||
if plan.branch in AIV_BRANCHES:
|
||||
if plan.used_core_num > s.aiv_num:
|
||||
@@ -77,9 +84,19 @@ def check_plan_constraints(case: BmmCase, plan: ImplPlan,
|
||||
|
||||
# --- dValue --- (issue#6 口径裁定: 只对"以 K 段为连续维"的方案生效)
|
||||
if _k_segment_is_contiguous(plan, case) and plan.k_l1 > 0:
|
||||
dv = plan.k_l1 * case.dtype_in_bytes
|
||||
if dv < s.dvalue_min:
|
||||
v.append(f"dValue={dv}B < 下限 {s.dvalue_min}B, K 段连续维搬移效率崩塌")
|
||||
if plan.branch == "IterBatch" and plan.l1_form.startswith(("c_", "d_")):
|
||||
# 转置感知判据与生成守卫/条件 4 同源 (issue#19):
|
||||
# dv_a = M*dt (A 转置) 或 k_l1*dt; dv_b = N*dt (B 不转置) 或 k_l1*dt;
|
||||
# 两侧连续维 dValue 均低于下限才算违规.
|
||||
from .models import dvalue_contig_dims
|
||||
dv_a, dv_b = dvalue_contig_dims(case, plan.k_l1)
|
||||
if dv_a < s.dvalue_min and dv_b < s.dvalue_min:
|
||||
v.append(f"dValueA={dv_a:.0f}B 与 dValueB={dv_b:.0f}B 均 < 下限 "
|
||||
f"{s.dvalue_min}B, 搬移连续维效率崩塌")
|
||||
else:
|
||||
dv = plan.k_l1 * case.dtype_in_bytes
|
||||
if dv < s.dvalue_min:
|
||||
v.append(f"dValue={dv}B < 下限 {s.dvalue_min}B, K 段连续维搬移效率崩塌")
|
||||
|
||||
# --- 写出 dtype --- (StreamK 部分和 4B 是正确行为)
|
||||
if plan.branch not in PARTIAL_SUM_4B_BRANCHES:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -55,6 +55,23 @@ def align_down(x: int, align: int) -> int:
|
||||
return (x // align) * align
|
||||
|
||||
|
||||
def dvalue_contig_dims(case: "BmmCase", k_l1: float) -> tuple:
|
||||
"""A/B 两侧 GM->L1 搬移的连续维 dValue (Byte) —— 依转置定连续维 (ND 排布).
|
||||
|
||||
A 不转置存储 [M,K]: 行内 K 连续, 切 K 后每行连续段 k_l1 -> k_l1*dt;
|
||||
A 转置存储 [K,M]: M 连续 -> m*dt;
|
||||
B 不转置存储 [K,N]: N 连续 -> n*dt;
|
||||
B 转置存储 [N,K]: 行内 K 连续, 切 K 后连续段 k_l1 -> k_l1*dt.
|
||||
|
||||
供三处共用 (issue#19): l1_form 生成守卫 / IterBatch 条件 4 / constraints 校验,
|
||||
保证"生成说可行、条件说可行、校验说可行"口径一致.
|
||||
"""
|
||||
dt = case.dtype_in_bytes
|
||||
dv_a = case.m * dt if case.trans_a else k_l1 * dt
|
||||
dv_b = k_l1 * dt if case.trans_b else case.n * dt
|
||||
return dv_a, dv_b
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Case 输入
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user