已收到问题,本地复现中


@qq_64858158 ,能麻烦补充一下Cast前后DumpTensor的打印信息吗


508dec6d651b4758a720796239de6a0b.gz
这边基于 example-49 做了一个简洁实现,未能成功复现,能否提供完整的测试工程


508dec6d651b4758a720796239de6a0b.gz
这边基于 example-49 做了一个简洁实现,未能成功复现,能否提供完整的测试工程
改动后还是出现该问题,现提供完整复线工程5feeee04a4dd40dba749650ed9aa903a.gz


崩溃问题修复报告
1. 问题概述
在 Ascend950PR (dav-3510) NPU 上,INT8 QK 量化的 attention kernel(4-deep cross-core pipeline)中,AscendC::Cast<float, int32_t> 在 AIV 侧 CrossCoreWaitFlag 之后执行时,从第 4 次 kvSeqLoop 迭代起(Sk≥512)间歇性崩溃,aclrtSynchronizeStream 返回 507015(task error)。
基线崩溃率(修复前,Sk=4096 实测):
| 测试轮次 | ok | crash |
|---|---|---|
| 10 runs | 1-2 | 8-9 |
| 5 runs(run.sh 统计) | ~20% | ~80% |
2. 硬件错误定位
通过 ASCEND_GLOBAL_LOG_LEVEL=1 + ASCEND_SLOG_PRINT_TO_STDOUT=1 获取的硬件错误信息(多轮崩溃模式一致):
AIC(Cube)— 原发错误
An error occurs on the device(chipId:0, dieId:0), serial is N, the error is
aicore error error, core id is 0, error code = 8,
pc start: 0x1200410024d0, current: 0x120041003XXX (随迭代变化),
cube error info: 0x11aa00000XXX (低16位随迭代变化),
l1 error info: 0xadfa000de52f (固定),
errcode:(8) errorStr: "A multi-bit ECC error occurs when CUBE reads L0A"
AIV(Vector)— 继发错误(core 0 和 core 1)
aivec error error, error code = 0,
vec error info: 0x41008f08003900ae (固定),
current: 0x120041007360 (固定 — 卡在 CrossCoreWaitFlag),
errcode:(0) errorStr: "timeout or trap error"
结论:AIC 先崩溃(serial 较小),AIV 因等待 AIC 置位 SYNC_C1_V1_FLAG 超时。AIC 的 L0A 多比特 ECC 错误(ECC 无法纠正的 SRAM 数据损坏)是原发原因。
3. 根因分析
3.1 4-deep cross-core pipeline 结构
每个 kvSeqLoop 迭代中 4 个流水级跨 AIC/AIV 并行:
| 流水级 | 执行核 | 触发条件 | 操作 |
|---|---|---|---|
| QK(stage 1) | AIC | notLastThreeLoop |
int8×int8 Cube → L0C → Fixpipe SPLIT_M → UB(int32) |
| Softmax(stage 2) | AIV | taskId > 0 |
Cast<int32→float> → Muls → softmax → Copy P→L1 |
| PV(stage 3) | AIC | taskId > 1 |
读 P from L1 → L0A → MMAD P×V → Fixpipe → UB |
| RescaleO(stage 4) | AIV | taskId > 2 |
读 PV 结果 → rescale O → 写 GM |
runInfo[taskId & 3] 环形缓冲 4 个 slot,流水线需 4 次迭代填满 → 解释 Sk≥512(4×128)才触发。
3.2 关键同步链
AIC QK: Fixpipe(L0C→UB) → CrossCoreSetFlag<PIPE_FIX>(SYNC_C1_V1_FLAG)
AIV: CrossCoreWaitFlag<PIPE_V>(SYNC_C1_V1_FLAG) → Cast → Muls → softmax
→ copyUb2L1P(MTE3: P→L1) → CrossCoreSetFlag<PIPE_MTE3>(SYNC_V1_C2_FLAG)
AIC PV: CrossCoreWaitFlag<PIPE_MTE1>(SYNC_V1_C2_FLAG) → MTE1(L1→L0A) → MMAD → Fixpipe
3.3 崩溃机制
Cast<float>(int32) 是 INT8 路径相对 Half baseline 的唯一 AIV 侧额外操作,显著拉长 softmax 流水级的 V pipe 占用时间。其连锁效应:
Cast 延迟 AIV softmax
→ 延迟 SYNC_V1_C2_FLAG 置位
→ 延迟 AIC PV 启动
→ 改变 AIC/AIV 跨核指令时序窗口
→ 触发硬件 L0A SRAM 读写时序违例(多比特翻转,ECC 不可纠正)
→ AIC task exception (errcode=8)
→ AIV CrossCoreWaitFlag 超时 (errcode=0)
→ aclrtSynchronizeStream 返回 507015
关键证据:
- AIV 侧增加延迟(Cast 前加
PipeBarrier<PIPE_V>)→ 崩溃率升至 100% - AIV 侧减少延迟(移除 softmax 后 PipeBarrier)→ 崩溃率无改善
- 说明 Cast 本身引入的延迟已处于竞态窗口内,任何进一步扰动都加剧问题
4. 实验记录(失败的尝试)
| # | 方案 | 修改点 | Sk=4096 崩溃率 | 结论 |
|---|---|---|---|---|
| 0 | Baseline(原始代码) | - | ~80-90% | - |
| 1 | PipeBarrier<PIPE_M> |
AIC PV 前 | ~80% | 无效 |
| 2 | PipeBarrier<PIPE_FIX> |
AIC QK 后 | ~80% | 微弱改善 |
| 3 | PipeBarrier<PIPE_FIX> + PIPE_M |
AIC QK 后 | 100% | 更差 |
| 4 | PipeBarrier<PIPE_V> |
AIV Cast 前 | 100% | 更差(增加 AIV 延迟) |
| 5 | 移除 softmax 后 PipeBarrier | AIV | ~90% | 更差 |
| 6 | 移除 Fixpipe deqScalar | copy_l0c_to_ub.hpp | 100% | 更差 |
| 7 | 修正 deqScalar UB(清零高位) | copy_l0c_to_ub.hpp | 100% | 更差 |
| 8 | Muls 吸收进 scaleValue | 消除 AIV Muls | ~90% | 无效 |
| 9 | QK L0A 偏移至 [32KB:64KB] | block_mmad_fai_qk_tla.hpp | 100% | 更差 |
| 10 | DEQF16(int32→half) + Cast(half→float) | Fixpipe 模式 + Cast | 100% | 更差 |
| 11 | PipeBarrier<PIPE_ALL> ×5 |
5 个流水交接点 | 0% | ✅ 修复 |
| 12 | PipeBarrier<PIPE_ALL> ×2(最终) |
AIV Cast 前 + AIC PV 后 | 0% | ✅ 修复 |
教训:单一子流水线 barrier(PIPE_V/M/FIX)无法消除跨核残留操作的并发;PIPE_ALL 排空核内全部子流水线(S/V/M/FIX/MTE1/MTE2/MTE3)才有效。
5. 最终修复方案
在 overlay/examples/sage_attn_fp8/kernel/sage_fai_kernel_int8.h 中保留 2 处 AscendC::PipeBarrier<PIPE_ALL>():
修复点 1(Line 358):AIV Cast 之前
CrossCoreWaitFlag<SYNC_MODE, PIPE_V>(SYNC_C1_V1_FLAG[taskIdMod2]); // 等待bmm1完成
AscendC::PipeBarrier<PIPE_ALL>(); // ★ 排空上一轮 softmax/RescaleO 残留在 V+MTE3 的操作
uint32_t totalSize = runInfo3.halfQSeqRealSize * kvSeqlenTemplateType;
constexpr float DEQUANT_SCALE = 1.0f / (127.0f * 127.0f);
AscendC::Cast(castFloatBuf, bmm1TensorList[taskIdMod2], AscendC::RoundMode::CAST_NONE, totalSize);
作用:上一轮 softmax 的 copyUb2L1P(MTE3:UB→L1 搬运 P)和 RescaleO 的 V 操作可能仍在执行。若 Cast 立即启动,其 UB 读写(读 bmm1TensorList、写 castFloatBuf)与遗留 MTE3 并发访问 UB/L1,改变跨核时序。此 barrier 保证 Cast 开始时 AIV 流水线完全干净。
修复点 2(Line 434):AIC PV 之后
CrossCoreSetFlag<SYNC_MODE, PIPE_FIX>(SYNC_C2_V2_FLAG[runInfo2.taskIdMod2]);
CrossCoreSetFlag<SYNC_MODE, PIPE_FIX>(16 + SYNC_C2_V2_FLAG[runInfo2.taskIdMod2]);
AscendC::PipeBarrier<PIPE_ALL>(); // ★ 排空 PV 的 Cube 残留,保证下一轮 QK 启动时 L0A/L0C 空闲
作用:PV 的 Fixpipe 写 UB 完成后立即排空整个 Cube 流水线(MTE1/M/FIX),下一轮 QK 启动时 L0A/L0C 无任何在途操作。
6. 验证结果
6.1 Sk=4096(32 loops,修复前 ~90% 崩溃)
| 配置 | 运行次数 | ok | crash |
|---|---|---|---|
| baseline | 10 | 1 | 9 |
| 5× PIPE_ALL | 30 | 30 | 0 |
| 2× PIPE_ALL(最终) | 30 | 30 | 0 |
6.2 完整 A/B 对比(run.sh,runs/shape=5)
Sk kvLoops Kernel Result (runs 5) Bug?
-------------------------------------------------------------------------
128 1 INT8 ok=5 crash=0 nan=0 timeout=0 no
Half ok=5 crash=0 nan=0 timeout=0 -
256 2 INT8 ok=5 crash=0 nan=0 timeout=0 no
Half ok=5 crash=0 nan=0 timeout=0 -
512 4 INT8 ok=5 crash=0 nan=0 timeout=0 no
Half ok=5 crash=0 nan=0 timeout=0 -
1024 8 INT8 ok=5 crash=0 nan=0 timeout=0 no
Half ok=5 crash=0 nan=0 timeout=0 -
4096 32 INT8 ok=5 crash=0 nan=0 timeout=0 no
Half ok=5 crash=0 nan=0 timeout=0 -
================================================================
RESULT: All passed (bug NOT reproduced).
================================================================
6.3 正确性
- 所有 shape 输出无 NaN(
NaN count: 0 / 16384) - Half baseline 不受影响
Cast<float>(int32)完整保留,数值路径不变
7. 性能影响与后续优化方向
- 影响:
PipeBarrier<PIPE_ALL>强制 AIV/AIC 核内全部子流水线串行化,4-deep pipeline 的部分并行度被牺牲,吞吐下降。 - 优化方向(如需恢复性能):
- 将
PIPE_ALL替换为更细粒度的跨核事件同步(SetFlag/WaitFlag仅针对 Cast 与 MTE3 的依赖) - 增大
castFloatBuf双缓冲,使 Cast 与上一轮 MTE3 天然错开,从而移除 barrier 1 - 验证能否仅保留 barrier 2(更小性能损失)
- 将


定位发现是算子读写抢占问题导致的崩溃,接口功能是正常的,建议先按照上述方法修复问题




Thanks for sending an issue! Please fill in the following template to help quickly solve your problem.
Describe the current behavior / 问题描述 (Mandatory / 必填)
在 mix kernel 的 AIV 侧,对 Fixpipe(SPLIT_M,
dualDstCtl=1)写入 UB 的 int32 数据调用Cast<float>(int32_t)做类型转换:Environment / 环境信息 (Mandatory / 必填)
CATLASS_GLOBAL,AIC+AIV 协同)Steps to reproduce the issue / 重现步骤 (Mandatory / 必填)
复现条件
Kernel 结构(简化):
// AIC 侧 BlockMmadTla<MmadFAIQK>(int8×int8 → int32 L0C) // Fixpipe: L0C int32 → UB int32 (SPLIT_M, dualDstCtl=1, NO_QUANT) CrossCoreSetFlag(SYNC_C1_V1_FLAG); // AIV 侧 CrossCoreWaitFlag(SYNC_C1_V1_FLAG); // ↓ 此处第3次迭代起输出NaN ↓ AscendC::Cast(floatBuffer, int32Buffer, RoundMode::CAST_NONE, 8192); AscendC::Muls(floatBuffer, floatBuffer, scale, 8192); // 传给后续 softmax epilogue完整上下文:这是一个 FlashAttention 4-deep pipeline(参考 CATLASS example-49 FAInferKernel),fork 后将 QK 从 half 改为 int8→int32。Cast 的 src 是
bmm1TensorList[taskIdMod2](Fixpipe SPLIT_M 写入的 UB),dst 是独立分配的castFloatBuf(不与任何其他 buffer 重叠)。排除项
int32.float()对 32768 元素Duplicate(0.0f)填充 dst关键对比实验
Describe the expected behavior / 预期结果 (Mandatory / 必填)
Cast<float, int32_t>应在任何迭代次数下正确转换(如 int32=128 → float=128.0f)。Related log / screenshot / 日志 / 截图 (Mandatory / 必填)
问题分类
涉及算子/API:
AscendC::Cast(Vector 类型转换指令)具体调用:
AscendC::Cast(dstFloatTensor, srcInt32Tensor, AscendC::RoundMode::CAST_NONE, count); // 或 CAST_ROUND,两者均复现疑似原因
Cast Vector 指令在经过
CrossCoreWaitFlag恢复后的第 3+ 次调用时,可能存在内部状态未正确重置的问题。具体表现为:当 Cast 的源数据由 Fixpipe(cross-core,AIC→AIV)写入、且经过多次 pipeline 迭代后,Cast 输出不正确。Special notes for this issue/备注 (Optional / 选填)