Closed
[Bug-Report|缺陷反馈]: Device machine性能优化 #2597
LordOfMysteriesAdd on  Jul 9Closed at  Sep 14
LordOfMysteriesCollaborators
Jul 9 Create

控制流性能优化需求开发文档

1. 背景与目标

控制流(Control Flow / AICPU)路径在多 Kernel 反复启动、表达式求值、Stitch/UpdateSlots 等高频场景中开销偏大,表现为:

  • AOT 控制流二进制反复 memcpy / clear_cache
  • SetExprBatch 内对相同 Shape/Data 查询重复展开
  • UpdateSlots 热路径存在堆分配与 hash 去重
  • MakeTask / Dup / Cache 备份存在过量初始化与整块清零
  • value-depend 场景仍构建无用的 host 侧 control-flow cache

本需求针对上述瓶颈做结构性优化,降低单次启动与热路径常数因子,不改变算子语义与精度预期。


2. 模块一:AOT 控制流代码池

2.1 改动前问题

原实现为单槽 BSS 代码池:每次构造控制流 AOT 时无条件将二进制 memcpy 进固定区域并 clear_cache。

多 Program / 多 Kernel 交替执行时,即便同一 hash 的控制流代码刚被拷入池中,再次启动仍会重复拷贝,MAPEXE_MEMCPY 成为固定开销。

2.2 改动原理

引入进程级多槽 hash 代码池(Device 16 槽 / Host·仿真 4 槽):

  • 每槽记录当前占用的 DevProg hashKey,并维护 LRU
  • EnsureCached(hash):hash 仍在池内 → 跳过 memcpy;否则选空槽或 LRU victim 再拷贝
  • 用 aotPoolSlotId 作为上次命中槽位 hint,加速查找
  • 池专用于控制流二进制;表达式不再与 CF 争用同一池

收益:同 Program 重复启动或池内尚未被覆盖时,消除冗余 AOT 拷贝与 I-cache 刷新。


3. 模块二:表达式公共子表达式消除(CSE)

3.1 改动前问题

SetExprBatch 与控制流中的表达式按树逐个展开。同一 batch / 同一 launch 内多次出现的:

  • RUNTIME_GetInputShapeDim(ARG_*, imm)
  • RUNTIME_GetInputData(ARG_*, RUNTIME_*, imm)

会被重复生成与执行,AICPU 侧无效调用偏多。

3.2 改动原理

两层 CSE,且仅对固定叶子入参生效(Immediate / ARG_* / RUNTIME_*;排除 VALUE_*、嵌套表达式):

  1. Launch CSE
    跨表达式表收集可复用的 ShapeDim,在 ControlFlowEntry 栈上计算一次 CSE_sd[],作为参数传入各 SetExprBatch。

  2. Batch-local CSE
    同一 batch 内相同目标 call 出现 ≥2 次时,在函数顶部提取 __cse_i,后续引用标识符而非再次展开。

因 AICPU 控制流只加载 .pypto section,不能依赖 namespace 全局变量,故采用栈数组 + 参数传递,而非静态全局。

收益:减少控制流与 SetExprBatch 中重复的 Shape/Data 查询,缩短表达式求值路径。


4. 模块三:CellMatch Tag 与 Slot 去重

4.1 改动前问题

  1. UpdateSlots 去重:用 unordered_set 对 partial update 去重 → 热路径堆分配 + hash。
  2. Tag 编码:将 devTaskId 等塞进 tag 低位,与长跑 task 计数强耦合,位宽易耗尽,且重置策略成本高。
  3. CellMatch 池初始化:曾在设备 Init 路径逐 word 填充,拉长启动。

4.2 改动原理

  1. Slot 字节 bitmap 去重

    • slotSize ≤ 2048:栈上 bitmap
    • 更大图:encode 期预预算 slotDedupBitmapSize,从 metadata.general 分配
    • 替代热路径 unordered_set
  2. 独立 cellMatchTagSeq 世代号

    • 随 PushTask 推进;接近上限时整池 reset 后从 0 重新编号
    • Stitch / UpdateSlots 统一传入该序列,与 taskId 解耦
  3. 池初始化前移

    • Host 侧 memset / tag 溢出时再 fill,避免每次 InitDyn 设备侧逐 word 循环

收益:降低 UpdateSlots/Stitch 的常数开销,稳定长跑下的 CellMatch 元数据管理。


5. 模块四:MakeTask / Dup / Cache 路径微优化

5.1 改动前问题

点 慢因
ItemPool 构造时一次性把全部 slot 链入 free list,大容量时 O(N) 初始化
DynDeviceTask 整颗 DeviceTask(含大 wrap 数组)先 memset 再写字段
DynFuncDataBackup 非平凡默认构造,stitch 上限规模下产生大量默认初始化 store
Ctrl-flow cache 备份 固定按 MAX_STITCH_FUNC_NUM 拷贝,而非实际使用条数
DuplicateRoot schema 拷贝后对整段 runtime tail 无条件 memset(大量字段稍后才赋值)

5.2 改动原理

  • ItemPool 懒扩展:首块预热,之后按需扩展 free list
  • InitDevTaskShell:只清「赋值前会被读」的字段,跳过 bulk wrap 数组
  • DynFuncDataBackup 平凡默构:去掉 NSDMI,减少数组默认构造开销
  • Cache backup/restore:按实际 dynFuncDataCacheListSize 拷贝
  • Dup:去掉整段 tail memset;workspace 分配前仅清 stitch Head 等必要状态

收益:压缩 MakeTask、Root Dup、cache 录制/回放路径的内存与初始化开销。


6. 模块五:Value-depend 跳过 Host Control-Flow Cache

6.1 改动前问题

Value-depend 图仍会:

  1. 在控制流中 emit CACHESTOP stitch
  2. Host 侧继续 BuildControlFlowCache / Emulation 录 cache

录完的 cache 对 value-depend 无实际复用价值,反而增加 host 启动与 emulation 成本,并引入无意义的 CACHESTOP 停顿语义。

6.2 改动原理

  • 仅置位 disableCtrlFlowCache,不再 emit CACHESTOP stitch
  • Host 入口统一短路:KernelBinary::BuildControlFlowCache、EmulationLauncher、Python DeviceRunOnce / BuildCache 等在 disable 时直接跳过构建

收益:value-depend 场景去掉无效 host cache 构建与 CACHESTOP 开销。

likedislike
LLordOfMysteriesCollaborators
Jul 9 Added label: bug-report
LLordOfMysteriesCollaborators
Jul 9 Assign LordOfMysteries as responsible person
LordOfMysteriesCollaborators
Jul 9 Comments:

需求跟踪

likedislike
LLordOfMysteriesCollaborators
Jul 9 Added label: Task; Removed label: bug-report
LordOfMysteriesCollaborators
Jul 10 Comments:
LLordOfMysteriesCollaborators
Jul 13 Modify description of issue
Zzyt123Collaborators
Jul 14 Associated pull request
LLordOfMysteriesCollaborators
Jul 14 Modify description of issue
LLordOfMysteriesCollaborators
Jul 14 Modify description of issue
LLordOfMysteriesCollaborators
Jul 17 Associated pull request
LLordOfMysteriesCollaborators
Jul 17 Modify description of issue
LordOfMysteriesCollaborators
Jul 20 Comments:

image.png

image.png

image.png

波动:
image.png

likedislike
LordOfMysteriesCollaborators
Jul 20 Comments:
LordOfMysteriesCollaborators
Jul 20 Comments:
LordOfMysteriesCollaborators
Jul 20 Comments:

堆栈保护代码膨胀:
image.png
image.png

image.png
efb2ca6e484c4f6daf414ceca4cbd7bb.txt

6cfbf58eb7194982a2d0aea17d4daab0.txt

4. Ctrl / Sche 热路径栈数组分布

说明:仅统计函数内本地数组;类成员数组(如 readyIds_[...])不触发“因本地数组而插桩”这一条。
device_ctrl.cpp / device_sche.cpp 本身几乎无本地栈数组;热点在 inline 头文件与 context 中。

4.1 Sche(调度)——优先处理

函数 文件 栈数组 规模 热度
AiCoreManager::ResolveDepForAllAiCore aicore_manager.h ResolveTaskContext resolveCtx[MAX_RESOLVE_TASK_NUM] 144(MAX_MANAGER_AIV_NUM * 2) [[gnu::hot]],每轮 resolve
AiCoreManager::TryBatchSendTask 同上 uint32_t readyId[MAX_MANAGER_AIV_NUM] 72 [[gnu::hot]],发任务前 dequeue
AiCoreManager::ResolveHubMixDepDyn 同上 uint32_t taskIds[MAX_WRAP_TASK_NUM] 3 mix 依赖解析
AiCoreManager::ResolveHubMixDepDynStitched 同上 uint32_t taskIds[MAX_WRAP_TASK_NUM] 3 mix stitch 依赖
WrapManager::DispatchReadyTasksImpl wrap_manager.h WrapInfo* localTasks[8] 8 DispatchMixCoreTask

相关常量(aicore_constants.h / machine_ws_intf.h / aikernel_data.h):

常量 值
MAX_AICORE_NUM 108
MAX_MANAGER_AIV_NUM 72
MAX_RESOLVE_TASK_NUM 144
MAX_WRAP_TASK_NUM 3
SCH_DEVTASK_MAX_PARALLELISM 8(1 << 3)
DEV_SHAPE_DIM_MAX 6

Sche 最大头:ResolveDepForAllAiCore 的 resolveCtx[144]。

stack.txt 中与 sche 对应的带 canary 符号包括:

  • AiCoreManager::TryBatchSendTask
  • AiCoreManager::BatchSendTask
  • AiCoreManager::ResolveDepForAllAiCore
  • AiCoreManager::ResolveHubMixDepDyn
  • AiCoreManager::ResolveDepDyn
  • WrapManager::ResolveDepForOneMix
  • WrapManager::PushTaskToTasklist
  • DeviceTaskContext::ProcessWrapQueue / ProcessAivBatchTasks / BuildReadyQueue* 等

4.2 Ctrl(stitch / slot / execute)

函数 文件 栈数组 规模 热度
DeviceStitchContext::PartialUpdateStitchConsumer device_stitch_context.cpp consumerOffset/ValidShape[DEV_SHAPE_DIM_MAX] 6×2 stitch 消费侧循环内
DeviceStitchContext::FullCoverDefaultUpdateStitch 同上 fullCoverOffset/ValidShape[6] 6×2 full-cover stitch
DeviceStitchContext::PartialUpdateStitchProducer 同上 producerOffset/ValidShape[6] 6×2 stitch 生产侧
CellMatchHandle / CellMatchFill* dev_encode_function_stitch.h rangeBegin/End[6]、offset/validShape[6] 6 cell-match 填充
PrepareRuntimeDynamicPartialUpdateTables device_slot_context.cpp uint8_t stackPreparedBySlot[2048] 2048 函数开头已 return;,当前近乎死代码

单次 [6] 体积不大,但 stitch 在 consumer/producer 循环内反复建栈帧,-strong 会稳定插桩。

4.3 次热点 / 冷路径

函数 栈数组 说明
InitAicoreParallelDevTask localFuncData/Ids[SCH_DEVTASK_MAX_PARALLELISM](8) 初始化
HandShakeByGmWithPreSendTask bool handFlag[MAX_AICORE_NUM](108) handshake
DumpAicoreLog char buf[512] dump
DeviceTrace 宏 char buf[MAX_MSG_LEN] 若宏打进热函数,会连带保护调用方
dump / prof / perf_event_sampler 各类 buf / counts 非调度主路径

5. 编码层面消除影响(建议优先级)

5.1 原则

  1. 热路径去掉本地数组(最有效)
  2. 避免无必要地取局部变量地址
  3. 大临时对象外提到成员 / TLS / 调用方缓冲
  4. 极少数已审计热点可用 __attribute__((no_stack_protector))(安全换性能,慎用)
likedislike
LordOfMysteriesCollaborators
Jul 21 Comments:

连续跑,ctrl间隔短性能就会更好:

去掉aicore执行:

[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.064.009 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 19115"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.064.751 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 11003"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.065.228 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7937"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.065.807 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7733"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.066.363 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8097"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.066.819 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7717"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.067.306 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 6923"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.067.806 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7662"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.068.274 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8002"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.068.756 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7153"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.069.257 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7429"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.069.711 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7412"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.070.211 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7071"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.070.719 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8122"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.071.176 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7384"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.071.659 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 6815"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.072.166 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7985"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.072.626 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7400"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.073.114 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 6852"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.073.664 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7898"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.074.173 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7607"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.074.713 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7133"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.075.261 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7960"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.075.728 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7748"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.076.219 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7047"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.076.731 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7846"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.077.195 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7387"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.077.741 [device_sche.h:370]11798 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7078"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.078.252 [device_sche.h:370]11796 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8217"
[ERROR] AICPU(11784,aicpu_scheduler):2026-07-21-09:42:56.078.720 [device_sche.h:370]11800 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 7492"

ctrl:间隔1.2ms 平均:7539

image.png

不去掉aicore执行:
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.686.081 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 18807"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.687.580 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8791"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.689.007 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9809"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.690.359 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8000"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.691.732 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8319"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.693.214 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9000"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.694.550 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8341"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.696.052 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9162"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.697.483 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8693"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.698.840 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8151"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.700.252 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9763"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.701.633 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8686"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.702.976 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8507"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.704.358 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8541"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.705.822 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8551"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.707.185 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8771"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.708.567 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9277"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.710.033 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8529"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.711.385 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9389"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.712.810 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9636"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.714.172 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8427"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.715.517 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8073"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.716.910 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8173"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.718.380 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8784"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.719.734 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9330"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.721.246 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9413"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.722.650 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9380"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.724.022 [device_sche.h:370]20531 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 9368"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.725.381 [device_sche.h:370]20529 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8037"
[ERROR] AICPU(20515,aicpu_scheduler):2026-07-21-09:47:33.726.841 [device_sche.h:370]20527 EntrySplittedStreamCtrl ErrCode: F00000! "Ctrl time: 8522"

ctrl:间隔1.2ms 平均:8807
image.png

一句话: 欧拉上能导致「越密越快」的,主要是 cpufreq/cpuidle 空闲降频与唤醒代价,加上 本核 cache/TLB/预测器变冷;不是 AICore 和 AICPU 共用一套 cache。

likedislike
LordOfMysteriesCollaborators
Jul 23 Comments:

image.png

likedislike
LordOfMysteriesCollaborators
Jul 24 Comments:

image.png

likedislike
LordOfMysteriesCollaborators
Jul 24 Comments:

正向:
image.png

image.png

反向:
image.png

perf:
21754bfca0394ad995758fecb44f60cb.json

likedislike
LLordOfMysteriesCollaborators
Jul 27 Associated pull request
LordOfMysteriesCollaborators
Jul 27 Comments:

models/glm_v4_5/glm_attention_fusion.py
优化前:
image.png

profiling后20轮平均值:416

优化后:

image.png

profiling后20轮平均值:223
likedislike
LLordOfMysteriesCollaborators
Jul 27 Associated pull request
LLordOfMysteriesCollaborators
Jul 27 Associated pull request
LordOfMysteriesCollaborators
Jul 28 Comments:

不同算子性能热点是有差别的,比如:
(1)models/glm_v4_5/glm_attention_fusion.py
image.png

(2)custom/chunk_kda/test_chunk_kda.py
image.png

波动:194.276 --> 232.435 37us(19%)

likedislike
LordOfMysteriesCollaborators
Jul 28 Comments:

后续:

1、当前优化性能看护:看下平均值波动。考虑CI和测试侧都加看护。
2、第二阶段性能优化:
	(1)编码优化 
    (2)大流程?? 数据结构访问优化,计算分担给aicore。
    (3)buildtask里的数据结构 
    (4)fast stitch用hub优化,可能会牺牲并行度 
    (5)tensor内存分配&&slot管理 梳理下  -- 杰神
3、ctrl cpu 性能波动分析:目前波动在20%。性能降下来波动应该会小,接整网带宽、资源竞争也可能影响。
4、不同算子性能热点不同,分析前端差异带来的性能差异点。
5、高性能编码skill完善、落地:https://gitcode.com/cann/pypto/pull/5097
6、资料补充下分析buildtask头开销的说明。msprof + aicpu perf结合分析。 -- 建芬
7、cellmatch优化完库上用例批跑分析下stitch性能,再定性能

8、场景  
  非值依赖:走host cache。
  值依赖:一个task;多个task。看首task开销。

likedislike
GgaoxingwangCollaborators
Aug 18 Associated pull request
gaoxingwangCollaborators
Aug 20 Comments:

PR 5531 优化点

perf(machine): Optimize stitch code

优化点清单

# 优化点 改动文件 改前 改后 优化类型
1 属性基址缓存 dev_callop_attribute.h 每次调用 3 次 GetOperationAttr 查表获取 offset/rawShape/validShape 指针 cachedAttrBase 懒加载 1 次 GetSymoffset,后续指针偏移直接算 热路径查表消除
2 结构体字段瘦身 dev_encode_operation.h shapeAttrIdx + wrapTaskHubOpIdx 两个字段 删除 shapeAttrIdx(改为 offsetAttrIdx+dim 按需算),wrapTaskHubOpIdx → stitchOpIdx(encode 期预赋值) 结构体缩小 + runtime 查表消除
3 offsetAttrIdx 预累加 dev_encode.cpp runtime 每次查 opAttrOffsetList_ 获取 opAttrBase encode 期 offsetAttrIdx += opAttrBase,runtime 直接读 以存代算
4 stitchOpIdx 预赋值 dev_encode.cpp runtime if (wrapTaskHubOpIdx != INVALID) 判断 + 赋值 encode 期 stitchOpIdx = (hub存在) ? hubIdx : operationIdx 以存代算 + 分支消除
5 构造函数参数减少 dev_encode.cpp emplace_back(opIdx, coaIndex, coaIndex+dimSize, ...) 传 3 个 index emplace_back(opIdx, coaIndex, ...) 传 2 个,shape 索引按需算 调用开销减少
6 skipExpression 模板消除 dev_callop_attribute.h template <bool skipExpression> 循环内 3×dim 次 if (skipExpression) 运行时分支 去掉模板参数和运行时分支 编译期分支消除
7 循环合并 dev_callop_attribute.h CheckOffsetAndValidShapeInRawShape 两个独立 for 循环 合并为一个循环 循环开销减半
8 IsCellMatchDescFillReady 删除 dev_callop_attribute.h runtime 每次遍历所有维度检查 cellShape/stride > 0 删除函数,host encode 期保证 desc 已填充 device 侧检查消除
9 Dump 隔离 device_slot_context.cpp / device_stitch_context.cpp / dump_device_topo.cpp DumpProducerCellAccess/DumpConsumerCellAccess 裸调用 DEV_IF_NONDEVICE 包裹,device 编译期排除 device 侧零开销
10 unlikely 标注 dev_callop_attribute.h if (clamped) 无分支预测提示 if (unlikely(clamped)) 分支预测优化
11 CellMatchProcessByDim 局部变量 dev_encode_function_stitch.h switch (cellMatchTableDesc.cellShape.dimSize) 每次读结构体字段 switch (dims) 用局部变量 循环不变量外提
12 CellMatchHandle dims 外提 dev_encode_function_stitch.h 循环内 cellMatchTableDesc.GetDimensionSize() 重复调用 提到循环外存 dims 循环不变量外提
13 Process 签名加 desc dev_encode_function_stitch.h 每个 Handler 内部重复读 cellMatchTableDesc 通过参数传入 const DevCellMatchTableDesc& desc 参数传递优化
14 runtime wrapTaskHubOpIdx 查表消除 device_stitch_context.cpp runtime if (consumer.wrapTaskHubOpIdx != INVALID) 判断 + 赋值 consumerOpIdx 直接用 consumer.stitchOpIdx runtime 分支消除
15 GetTensorOffsetAndValidShape 调用优化 device_stitch_context.cpp 传 operationIndex + offsetAttrIdx,函数内部查 GetOperationAttr 传 offsetAttrIdx + cachedAttrBase,函数内部用缓存指针 热路径查表消除

性能数据

场景 方向 收益
FAST_STITCH bwd -4.6%
UPDATE_SLOT bwd -3.0%
DEV_TASK_BUILD first -3.1% ~ -3.6%
likedislike
GgaoxingwangCollaborators
Aug 20 Associated pull request
gaoxingwangCollaborators
Aug 21 Comments:

PR 5584 优化:

GDR 算子 Stitch 遍历开销分析与优化方案

采集方法:单轮测试,device 日志中 DEV_ERROR 统计。
采集时间:2026-08-07(fwd)/ 2026-08-08(bwd)
pypto: commit2 43edc032a(run 包安装,device 侧代码编译生效)


1. RootFunc 结构

fwd — varlen_T32K_H8(234 轮,3 个 rootfunc)

funcKey outcastSize incastSize outSlots outPartial outNonPartial inSlots inPartial inNonPartial
0 4 0 4 0 4 0 0 0
1 5 12 5 1 4 12 0 12
2 5 12 5 1 4 12 0 12

每轮总计:outSlots=14(partial=2, nonPartial=12),inSlots=24(partial=0, nonPartial=24)。

bwd — varlen64_t32k_h8_bt64(4784 轮,多个 rootfunc 循环)

funcKey outcastSize incastSize outSlots outPartial outNonPartial inSlots inPartial inNonPartial
4 2 5 2 1 1 5 0 5
0 2 6 2 1 1 6 0 6
8 2 6 2 1 1 6 0 6
25 2 7 2 1 1 7 0 7

每轮总计:outSlots=2(partial=1, nonPartial=1),inSlots=5~7(partial=0, nonPartial=5~7)。


2. 全集遍历统计

fwd — varlen_T32K_H8(234 轮累积)

UpdateSlotsImpl

分类 次数 说明
outTotal 3392 outcast→toSlotList 总遍历
├ outPartialWarWaw 576 partial + WAR/WAW → PartialStitch
└ outNonPartial 2816 nonPartial → FullCoverStitch
inTotal 6912 incast→fromSlotList 总遍历
└ inProcess 0 100% 无效(全 nonPartial,无 RAW)

PartialStitch / FullCoverStitch

函数 called 空跑 filled
PartialStitch 576 0 576(100% 有效)
FullCoverStitch 2816 0 2816(100% 有效)

FastStitchConsumer / FastStitchProducer

函数 total skipInvalidIdx skipNonPartial skipNoRawWaw process
FastStitchConsumer 6804 4536 0 0 2268
FastStitchProducer 3339 2 2772 0 565

bwd — varlen64_t32k_h8_bt64(4784 轮累积)

UpdateSlotsImpl

分类 次数 说明
outTotal 9568 outcast→toSlotList 总遍历
├ outPartialWarWaw 7136 partial + WAR/WAW → PartialStitch
└ outNonPartial 2432 nonPartial → FullCoverStitch
inTotal 23808 incast→fromSlotList 总遍历
└ inProcess 704 partial + RAW(有效,非 0)
└ 无效 23104 nonPartial 跳过(97% 无效)

PartialStitch / FullCoverStitch

函数 called 空跑 filled
PartialStitch 7136 0 7136(100% 有效)
FullCoverStitch 2432 0 2432(100% 有效)

FastStitchConsumer / FastStitchProducer

函数 total skipInvalidIdx skipNonPartial skipNoRawWaw process
FastStitchConsumer 23444 22042 0 0 1402
FastStitchProducer 9421 5 2394 0 7022

3. 无效遍历汇总

fwd

空跑类型 次数 根因
UpdateSlotsImpl incast nonPartial 6912 slot 全是 nonPartial,无 RAW 依赖
FastStitchConsumer skipInvalidIdx 4536 stitchDupIdx 未设置(无前驱)
FastStitchProducer skipNonPartial 2772 slot 是 nonPartial
总计 14220 /

有效遍历:576 + 2816 + 2268 + 565 = 6225。无效/有效 = 2.3:1。

bwd

空跑类型 次数 根因
UpdateSlotsImpl incast nonPartial 23104 97% incast slot 是 nonPartial
FastStitchConsumer skipInvalidIdx 22042 stitchDupIdx 未设置(无前驱)
FastStitchProducer skipNonPartial 2394 slot 是 nonPartial
总计 47540 /

有效遍历:7136 + 2432 + 704 + 1402 + 7022 = 18696。无效/有效 = 2.5:1。

fwd vs bwd 对比

指标 fwd bwd bwd/fwd
outTotal 3392 9568 2.8x
inTotal 6912 23808 3.4x
inProcess 0 704 bwd 有 RAW
无效遍历总计 14220 47540 3.3x
FastStitchConsumer skipInvalidIdx 4536 22042 4.9x
FastStitchProducer skipNonPartial 2772 2394 0.9x

4. 优化方案:预计算 slot 列表

设计原则:按"消费者需要的筛选条件"划分列表,而非按"数据属性"划分。每个列表只包含其消费者需要的 slot,不多不少。
所有 4 个列表均在 encode 阶段构建:slot 的 stitchCtrlBitMask 在 encode 阶段确定(partial slot 和 fullcover slot 都计算)。
首个 rootfunc 跳过:FastStitch 入口已有 devNextIdx == 0 检查(首个 rootfunc 无前驱),stitchIncastSlotList 和 stitchOutCastSlotList 的消费者在 FastStitch 内,devNextIdx == 0 时整体跳过,不遍历任何列表。

前置改造1:DevAscendProgramPartialUpdate → DevAscendProgramUpdate + isPartial

// dev_encode_function_stitch.h
struct DevAscendProgramUpdate {  // 原 DevAscendProgramPartialUpdate
    int slotIndex{-1};
    StitchCtrlBitMask stitchCtrlBitMask{STITCH_CTRL_NONE};
    bool isPartial{false};  // true=partial slot, false=fullcover slot
    DevCellMatchTableDesc cellMatchTableDesc;
    DevRelocVector<uint64_t> cellMatchRuntimePartialUpdateTable;
    bool Empty() const { return cellMatchRuntimePartialUpdateTable.size() == 0; }
};

DevAscendProgram::partialUpdateList 同步重命名为 updateList。InitPartialUpdateSlot 重命名为 InitUpdateSlot。

前置改造2:DeviceExecuteSlot 新增 stitchCtrlBitMask

// dev_encode_program_ctrlflow_cache.h
struct DeviceExecuteSlot {
    // ... 现有成员 ...
    StitchCtrlBitMask stitchCtrlBitMask{STITCH_CTRL_NONE};
    DevAscendProgramUpdate* partialUpdate{nullptr};  // 类型改为 DevAscendProgramUpdate*
};

MarkPartialUpdateSlots 改造——统一赋值 stitchCtrlBitMask,用 isPartial 区分:

// device_slot_context.cpp
slotList[slotIndex].stitchCtrlBitMask = update.stitchCtrlBitMask;
if (update.isPartial) {
    slotList[slotIndex].isPartialUpdateStitch = true;
    slotList[slotIndex].partialUpdate = &update;
}

前置改造3:SlotMaskEntry 结构体(dev_encode_function.h)

mask 计算结果的数据载体,在 RunEncodeStage 中作为局部变量,贯穿 RefilterSlotLists 和 InitUpdateSlot:

struct SlotMaskEntry {
    uint8_t mask{0};
    bool isPartial{false};
    uint32_t maxReadCount{0};
};

数据结构

在 dev_encode_function.h 中新增 2 个 entry 结构体 + 4 个 DevLocalVector:

struct OutCastSlotEntry {
    int slotIdx;
    int outcastIdx;   // 关联的 outcast 索引,避免运行时查找
};

struct IncastSlotEntry {
    int slotIdx;
    int incastIdx;    // 关联的 incast 索引,避免运行时查找
};

// 列表1: UpdateSlotsImpl outcast 循环用(全集,运行时按标记判断是否做 cell match fill)
// encode 不筛选,运行时还有其他处理(RuntimeOutcastTensorAssign 等)不能按标记排除
DevLocalVector<OutCastSlotEntry> updateOutCastSlotList;

// 列表2: FastStitchConsumer 用(写后读的 consumer stitch)
// encode 阶段用 stitchCtrlBitMask & WAR 筛选,排除没有写后读场景的 slot
DevLocalVector<IncastSlotEntry> stitchIncastSlotList;

// 列表3: UpdateSlotsImpl incast 循环用(读后写的 consumer fill)
// encode 阶段用 stitchCtrlBitMask & RAW 筛选
DevLocalVector<IncastSlotEntry> updateIncastSlotList;

// 列表4: FastStitchProducer 用(写后写+读后写的 producer stitch)
// encode 阶段用 stitchCtrlBitMask & (RAW|WAW) 筛选
DevLocalVector<OutCastSlotEntry> stitchOutCastSlotList;

场景映射

列表 场景 消费者 encode 筛选条件 运行时判断
updateOutCastSlotList 写后写 + 写后读(producer fill) UpdateSlotsImpl outcast 无(全集) 按 slot.stitchCtrlBitMask 判断是否做 cell match fill
stitchIncastSlotList 写后读(consumer stitch) FastStitchConsumer mask & WAR 无需 continue
updateIncastSlotList 读后写(consumer fill) UpdateSlotsImpl incast isPartial && (mask & RAW) 无需 continue
stitchOutCastSlotList 写后写 + 读后写(producer stitch) FastStitchProducer isPartial && (mask & (RAW|WAW)) 无需 continue

列表3/4 必须加 isPartial 条件:UpdateSlotsForIncastStitch 和 PartialUpdateStitchProducer 只处理 partial slot,fullcover slot 不能进入这两个列表。SlotMaskEntry 中同时存 isPartial 标记。

encode 判断逻辑(最终实现:两阶段 + 单源 mask)

原方案设想在 InitIncastOutcast 中直接从 partialUpdateList 构建 slotMaskMap 并筛选 4 列表。实际实现改为两阶段,因为 CalculateStitchCtrlBitMaskForSlot 依赖 devEncodeList(全部 root 编码后才有完整数据),不能在 per-root 的 InitIncastOutcast 中调用。

阶段一:编码循环内 — 保守填充(InitSlotPrefilterLists,per-root)

在 InitIncastOutcast 末尾调用 InitSlotPrefilterLists,遍历 outcast→outcastSlot 和 incast→incastSlot,不做任何 mask 筛选,把所有 slot 全部加入 4 列表(全集):

// dev_encode_function.h 声明 / dev_encode.cpp 实现
void DevAscendFunction::InitSlotPrefilterLists(initOffset, slot, incastTensorList, outcastTensorList, fillContent)
{
    // 计算全集 count
    for (outcast) { updateOutCnt += size; stitchOutCnt += size; }
    for (incast)  { stitchInCnt  += size; updateInCnt  += size; }

    // 分配空间(update 组 + stitch 组分组排列)
    updateOutCastSlotList.HostInitDataSizeOffset(initOffset, updateOutCnt);
    updateIncastSlotList.HostInitDataSizeOffset(initOffset, updateInCnt);
    stitchIncastSlotList.HostInitDataSizeOffset(initOffset, stitchInCnt);
    stitchOutCastSlotList.HostInitDataSizeOffset(initOffset, stitchOutCnt);

    // 填充 entry(全集)
    ONFILLCONTENT {
        for (outcast) { At(updateOutCastSlotList, uo++) = {slotIdx, index};
                        At(stitchOutCastSlotList, so++) = {slotIdx, index}; }
        for (incast)  { At(updateIncastSlotList, ui++) = {slotIdx, index};
                        At(stitchIncastSlotList, si++) = {slotIdx, index}; }
    }
}

为什么保守? CalculateStitchCtrlBitMaskForSlot 内部调用 CalculateSlotMaxReadCount,后者通过 devEncodeListInput[funcKey] reinterpret_cast 成 DevAscendFunction*,读取 outcast.producerConsumerList 中的 opType == READ。这要求所有 root 的 DevAscendFunction 已编码完成,而 InitIncastOutcast 在 per-root 编码循环内执行,此时 devEncodeList 尚未全部完成。

阶段二:编码循环后 — 构建 slotMaskMap + 过滤 + 传递(RunEncodeStage 末尾)

编码循环结束后,devEncodeList 完整可用。在 RunEncodeStage(backend.cpp)中:

// backend.cpp — RunEncodeStage 末尾
auto slotMaskMap = dynamic::BuildSlotMaskMap(attr);      // 1. 算 mask(唯一计算点)
for (auto& devRoot : attr->funcGroup.devRootList) {
    int devRootKey = attr->funcGroup.devRootList.GetIndex(devRoot);
    DevAscendFunction* funcBin = reinterpret_cast<DevAscendFunction*>(&attr->devEncodeList[devRootKey][0]);
    funcBin->RefilterSlotLists(slotMaskMap);              // 2. 过滤 4 列表
}
SetDyndevProgBinary(function, disableCtrlFlowCache, &slotMaskMap);  // 3. 传给 InitUpdateSlot

slotMaskMap 是 RunEncodeStage 的局部变量(不存入 DyndevFunctionAttribute),贯穿三个消费点。

BuildSlotMaskMap(dev_encode.cpp)— 唯一 mask 计算点
std::unordered_map<int, SlotMaskEntry> BuildSlotMaskMap(const std::shared_ptr<DyndevFunctionAttribute>& dyndevAttr)
{
    std::unordered_map<int, SlotMaskEntry> slotMaskMap;
    // 构建 kernelInputAssembleSlots(input ∩ assemble,用于 rootFuncOk gate)
    // 构建 partialSlotSet(用于 isPartial 判定)
    for (int slotIndex = 0; slotIndex < inoutLink.totalSlot; slotIndex++) {
        uint32_t maxReadCount = 0;
        uint8_t mask = CalculateStitchCtrlBitMaskForSlot(
            slotIndex, dyndevAttr->devEncodeList, dyndevAttr->rootFuncKeyDict,
            dyndevAttr->slotRootIncastDict, dyndevAttr->slotRootOutcastDict,
            kernelInputAssembleSlots, &maxReadCount);
        if (mask == STITCH_CTRL_NONE) { continue; }
        slotMaskMap[slotIndex] = {mask, partialSlotSet.count(slotIndex) != 0, maxReadCount};
    }
    return slotMaskMap;
}

CalculateStitchCtrlBitMaskForSlot 的 mask 计算逻辑:

  1. hasNormalAttr:遍历 slotRootOutcastDict[slotIndex] 中所有 root 的 outcast,检查 HasAttr("NORMAL")
  2. maxReadCount:CalculateSlotMaxReadCount — incastCount + outcastConsumerCount(outcast 中 opType == READ 的数量),依赖 devEncodeList 读取已编码的 DevAscendFunction outcast 数据
  3. rootFuncOk gate:仅对 kernelInputAssembleSlots(input ∩ assemble)中的 slot 检查 CountSlotRootFuncNum >= 2
  4. mask 赋值:hasNormalAttr → WAW,maxReadCount > 0 → WAR|RAW
RefilterSlotLists(dev_encode.cpp,DevAscendFunction 方法)— 原地压缩过滤
void DevAscendFunction::RefilterSlotLists(const std::unordered_map<int, SlotMaskEntry>& slotMaskMap)
{
    auto keepWar = [&](int slotIdx) {
        auto it = slotMaskMap.find(slotIdx);
        return it != slotMaskMap.end() && (it->second.mask & STITCH_CTRL_WAR);
    };
    auto keepRawPartial = [&](int slotIdx) {
        auto it = slotMaskMap.find(slotIdx);
        return it != slotMaskMap.end() && it->second.isPartial && (it->second.mask & STITCH_CTRL_RAW);
    };
    auto keepRawWawPartial = [&](int slotIdx) {
        auto it = slotMaskMap.find(slotIdx);
        return it != slotMaskMap.end() && it->second.isPartial
               && (it->second.mask & (STITCH_CTRL_RAW | STITCH_CTRL_WAW));
    };

    auto compact = [&](auto& list, auto&& pred) {
        size_t writeIdx = 0;
        for (size_t readIdx = 0; readIdx < list.size(); readIdx++) {
            auto& entry = At(list, static_cast<int>(readIdx));
            if (pred(entry.slotIdx)) {
                At(list, static_cast<int>(writeIdx)) = entry;
                writeIdx++;
            }
        }
        list.AssignOffsetSize(list.Offset(0), writeIdx);
    };

    compact(stitchIncastSlotList, keepWar);
    compact(updateIncastSlotList, keepRawPartial);
    compact(stitchOutCastSlotList, keepRawWawPartial);
    // updateOutCastSlotList 不过滤(全集)
}

原地压缩安全性:

  • DevLocalVector::AssignOffsetSize(offset, newSize) 只改 offset_ 和 size_,不改底层数据
  • 压缩是前向双指针(writeIdx <= readIdx),不会覆盖未读数据
  • DevAscendFunction::Reloc 是 no-op(dev_encode_function.h:232),不影响 DevLocalVector 的 offset
  • 保守列表分配了足够空间,过滤后 size_ 只会变小,不会越界
InitUpdateSlot — 从 slotMaskMap 直接取 mask(不再重复计算)

SetDyndevProgBinary → EncodeDevAscendProgram → Init → InitUpdateSlot,slotMaskMap 通过指针一路传递。InitSinglePartialUpdateSlot / InitSingleFullCoverSlot 直接从 slotMaskMap 取 mask/isPartial/maxReadCount,不再调 CalculateStitchCtrlBitMaskForSlot:

// InitSingleFullCoverSlot — 从 slotMaskMap 取
auto it = slotMaskMap->find(slotIndex);
if (it == slotMaskMap->end() || it->second.isPartial) { return; }  // 非 fullcover 跳过
uint8_t stitchCtrlBitMask = it->second.mask;

// InitSinglePartialUpdateSlot — 从 slotMaskMap 取
auto maskIt = slotMaskMap->find(slotIndex);
if (maskIt == slotMaskMap->end() || !maskIt->second.isPartial) {  // 非 partial 跳过
    FillEmptyPartialUpdateStub(prog, slotIndex); return 0;
}
uint8_t stitchCtrlBitMask = maskIt->second.mask;
uint32_t maxReadCount = maskIt->second.maxReadCount;

size-only pass 保护:EncodeDevAscendProgramSizeOnly(base == nullptr)时 slotMaskMap == nullptr,InitUpdateSlot 中 if (slotMaskMap == nullptr) { return; } 提前返回。

mask 一致性保证(单源)

CalculateStitchCtrlBitMaskForSlot 只在 BuildSlotMaskMap 中调用一次,结果存入局部 slotMaskMap,被两个消费方共用:

  1. RefilterSlotLists — 过滤 4 列表
  2. InitSinglePartialUpdateSlot / InitSingleFullCoverSlot(通过 InitUpdateSlot)→ updateList[i].stitchCtrlBitMask

因此 4 列表的筛选条件与 updateList 的 mask(device 侧 MarkPartialUpdateSlots 赋值给 slot.stitchCtrlBitMask)完全一致,且 mask 只算一次。

时机依赖

RunEncodeStage (backend.cpp)
├── for each devRoot:
│   └── EncodeDevAscendFunction → InitIncastOutcast → InitSlotPrefilterLists
│       └── 阶段一:保守填充 4 列表(全集,不筛选)
│           ⚠️ 此时 devEncodeList 尚未全部完成,不能调 CalculateStitchCtrlBitMaskForSlot
│
├── slotMaskMap = BuildSlotMaskMap(attr)   ← 全部 root 编码后,devEncodeList 完整
│   └── CalculateStitchCtrlBitMaskForSlot(唯一计算点)
│
├── for each devRoot:
│   └── funcBin->RefilterSlotLists(slotMaskMap)  ← 阶段二:压缩过滤 4 列表
│
└── SetDyndevProgBinary(function, disableCtrlFlowCache, &slotMaskMap)
    └── EncodeDevAscendProgram(func, offset, base, &slotMaskMap)
        └── InitUpdateSlot(..., slotMaskMap)
            ├── InitSinglePartialUpdateSlot  ← 从 slotMaskMap 取 mask/isPartial/maxReadCount
            └── InitSingleFullCoverSlot      ← 从 slotMaskMap 取 mask
                → updateList[slotIndex].stitchCtrlBitMask(与 slotMaskMap 同源)

关键约束:

  • BuildSlotMaskMap 必须在编码循环后(devEncodeList 完整)
  • RefilterSlotLists 必须在 BuildSlotMaskMap 后
  • InitUpdateSlot 接收 slotMaskMap 指针,size-only pass 传 nullptr

一对多扁平化说明

incast/outcast 与 slot 是一对多关系——incastSlot[idx] 和 outcastSlot[idx] 都是 vector<int>,一个 incast/outcast 可关联多个 slot。

原代码外层循环遍历 incast/outcast,GetIncast(i) / GetOutcast(i) 在外层取一次,内层遍历 fromSlotList / toSlotList 复用。

扁平化后同一个 outcastIdx / incastIdx 出现在多个 entry 中,每个 entry 各自调 GetOutcast(entry.outcastIdx) / GetIncast(entry.incastIdx)。但 GetOutcast / GetIncast 是 DevLocalVector 的 At(outcastList, index) 指针偏移计算,内联后为一条地址计算指令,开销可忽略。连续相同 outcastIdx / incastIdx 的 entry 编译器会 CSE(公共子表达式消除)复用前一次结果。

device 侧消费改造

原则:encode 阶段已做的 stitchCtrlBitMask 和 isPartialUpdateStitch 判断在运行时去掉,其他运行时判断(stitchDupIdx、越界保护等)保留。

UpdateSlotsImpl(device_slot_context.cpp):

// outcast 循环改为遍历 updateOutCastSlotList(全集):
for (size_t i = 0; i < devRootSrc->updateOutCastSlotList.size(); ++i) {
    auto& entry = devRootSrc->At(devRootSrc->updateOutCastSlotList, i);
    auto& outcast = devRootSrc->GetOutcast(entry.outcastIdx);
    auto& slot = slotList[entry.slotIdx];
    // 以下对所有 slot 执行(保持现有逻辑,不按标记排除):
    slot.stitchDupIdx = devNextIdx;
    slot.stitchOutcastIdx = static_cast<uint32_t>(entry.outcastIdx);
    DEV_IF_NONDEVICE { topo_dump::DumpProducerCellAccess(...); }
    workspace->RuntimeOutcastTensorAssign(slot.rtOutcastIter, outcastDesc.GetRtOutcastIter());

    // cell match fill:用 slot.stitchCtrlBitMask 判断是否有 WAR|WAW 场景
    // 用 slot.isPartialUpdateStitch 判断 partial/fullcover 分流(替代原 isPartialUpdateStitch + partialUpdate->stitchCtrlBitMask)
    uint32_t cellMatchTagId = CellMatchBuildTagId(slot.slotAllocIterId, cellMatchTagSeq);
    uint32_t errCode = 0;
    if (slot.stitchCtrlBitMask & (STITCH_CTRL_WAR | STITCH_CTRL_WAW)) {
        if (slot.isPartialUpdateStitch) {  // partial slot → PartialStitch fill
            if (slot.partialUpdate->cellMatchRuntimePartialUpdateTable.size() == 0) {
                PrepareRuntimeDynamicPartialUpdateTable(workspace, slot.partialUpdate);
            }
            auto producerList = &devRootSrc->At(outcast.producerConsumerList, 0);
            errCode = UpdateSlotsForOutCastPartialStitch(...);
        } else {  // fullcover slot + WAR|WAW → FullCover fill
            errCode = UpdateSlotsForOutCastFullCoverStitch(...);
        }
    }
    // slot.stitchCtrlBitMask == NONE → 跳过 cell match fill(但仍做了上面的其他处理)
}

// incast 循环改为遍历 updateIncastSlotList(已筛选 isPartial && RAW):
for (size_t i = 0; i < devRootSrc->updateIncastSlotList.size(); ++i) {
    auto& entry = devRootSrc->At(devRootSrc->updateIncastSlotList, i);
    auto& incast = devRootSrc->GetIncast(entry.incastIdx);
    auto& slot = slotList[entry.slotIdx];
    // 直接调 UpdateSlotsForIncastStitch(已筛选 isPartial && RAW,无需判断 isPartialUpdateStitch / stitchCtrlBitMask)
    uint32_t errCode = UpdateSlotsForIncastStitch(...);
}

FastStitchConsumer(device_stitch_context.cpp):

// 改为遍历 stitchIncastSlotList(已筛选 WAR):
for (size_t i = 0; i < nextSrc->stitchIncastSlotList.size(); ++i) {
    auto& entry = nextSrc->At(nextSrc->stitchIncastSlotList, i);
    auto slotIdx = entry.slotIdx;
    if (slotIdx >= (int)slotSize) { continue; }  // 越界保护(保留)
    auto& incast = nextSrc->GetIncast(entry.incastIdx);
    auto& slot = slotList[slotIdx];
    if (slot.stitchDupIdx == INVALID_STITCH_IDX) { continue; }  // 运行时保护(保留)
    // stitchCtrlBitMask 已在 encode 阶段筛选(WAR),无需判断
    // isPartialUpdateStitch 仍需判断(区分 partial → PartialUpdateStitchConsumer,nonPartial → FullCoverUpdateStitch)
    if (slot.isPartialUpdateStitch) {
        matchCount = PartialUpdateStitchConsumer(...);
    } else {
        if (slot.rtOutcastIter == ITEM_POOL_INVALID_INDEX) { continue; }  // 保留
        matchCount = FullCoverUpdateStitch(...);
    }
}

注意:FastStitchConsumer 中 isPartialUpdateStitch 保留——列表2 同时包含 partial+WAR 和 fullcover+WAR 的 slot,运行时需按 isPartialUpdateStitch 分流到不同处理函数。去掉的只是 stitchCtrlBitMask & WAR 判断(已在 encode 筛选)和 stitchCtrlBitMask 相关的 skipNonPartial/skipNoRawWaw 判断(不存在于此函数原逻辑中)。

FastStitchProducer(device_stitch_context.cpp):

// 改为遍历 stitchOutCastSlotList(已筛选 isPartial && (RAW|WAW)):
for (size_t i = 0; i < nextSrc->stitchOutCastSlotList.size(); ++i) {
    auto& entry = nextSrc->At(nextSrc->stitchOutCastSlotList, i);
    auto slotIdx = entry.slotIdx;
    if (slotIdx >= (int)slotSize) { continue; }  // 越界保护(保留)
    auto& outcast = nextSrc->GetOutcast(entry.outcastIdx);
    auto& slot = slotList[slotIdx];
    if (slot.stitchDupIdx == INVALID_STITCH_IDX) { continue; }  // 运行时保护(保留)
    // isPartialUpdateStitch 和 stitchCtrlBitMask 已在 encode 阶段筛选(isPartial && RAW|WAW),无需判断
    matchCount += PartialUpdateStitchProducer(...);
}

冗余遍历消除对照

fwd

无效遍历 次数 解决方式 列表
UpdateSlotsImpl incast nonPartial 6912 updateIncastSlotList 只含 isPartial && (mask & RAW) updateIncastSlotList
FastStitchConsumer skipInvalidIdx 4536 stitchIncastSlotList 只含 stitchCtrlBitMask & WAR,排除无写后读场景的 slot stitchIncastSlotList
FastStitchProducer skipNonPartial 2772 stitchOutCastSlotList 只含 `isPartial && (mask & (RAW WAW))`
总计 14220 全部清零

bwd

无效遍历 次数 解决方式 列表
UpdateSlotsImpl incast nonPartial 23104 updateIncastSlotList 只含 isPartial && (mask & RAW) updateIncastSlotList
FastStitchConsumer skipInvalidIdx 22042 stitchIncastSlotList 只含 stitchCtrlBitMask & WAR,排除无写后读场景的 slot stitchIncastSlotList
FastStitchProducer skipNonPartial 2394 stitchOutCastSlotList 只含 `isPartial && (mask & (RAW WAW))`
总计 47540 全部清零

updateOutCastSlotList 不筛选(fwd 3392 次 / bwd 9568 次,全有效),无冗余。

列表与场景映射

场景 UpdateSlotsImpl 消费 FastStitch 消费
WAW/写后写 updateOutCastSlotList(partial+WAW 分支) stitchOutCastSlotList
WAR/写后读 updateOutCastSlotList(partial+WAR + fullcover+WAR 分支) stitchIncastSlotList
RAW/读后写 updateIncastSlotList stitchOutCastSlotList

改动范围(实现版)

文件 改动
dev_encode_function_stitch.h DevAscendProgramPartialUpdate → DevAscendProgramUpdate,新增 isPartial
dev_encode_function.h 新增 SlotMaskEntry + OutCastSlotEntry/IncastSlotEntry + 4 个 DevLocalVector + accessor + RefilterSlotLists/InitSlotPrefilterLists 声明
dev_encode_function.cpp 4 列表 dump
dev_encode_function_param.h #include <cstdint>
dev_encode_program.h partialUpdateList → updateList,InitPartialUpdateSlot → InitUpdateSlot(参数精简),friend 声明更新
dev_encode_program.cpp dump 更新(isPartial)
dev_encode_program_ctrlflow_cache.h DeviceExecuteSlot 新增 stitchCtrlBitMask,partialUpdate 类型改为 DevAscendProgramUpdate*
dev_encode.h BuildSlotMaskMap 声明 + EncodeDevAscendProgram 加 slotMaskMap 参数
dev_encode.cpp InitSlotPrefilterLists + CalculateStitchCtrlBitMaskForSlot + BuildSlotMaskMap + RefilterSlotLists + InitSingleFullCoverSlot/InitSinglePartialUpdateSlot(从 slotMaskMap 取 mask)+ InitUpdateSlot(合并遍历)
dev_encode_workspace.cpp DevAscendProgramUpdate 重命名
device_slot_context.cpp MarkPartialUpdateSlots 改造 + UpdateSlotsImpl 遍历预计算列表 + 统计日志
device_stitch_context.cpp FastStitchConsumer/FastStitchProducer 遍历预计算列表 + 统计日志
backend.cpp RunEncodeStage 末尾 BuildSlotMaskMap + RefilterSlotLists + SetDyndevProgBinary 传 slotMaskMap
test_cell_match_dynamic.cpp DevAscendProgramUpdate 重命名
test_dev_encode_function_stitch.cpp DevAscendProgramUpdate 重命名

风险与解决

  • InitSingleFullCoverSlot 的分析逻辑需与 InitSinglePartialUpdateSlot 完全一致 → 已解决:抽取公共函数 CalculateStitchCtrlBitMaskForSlot,BuildSlotMaskMap 调用一次,两者都从 slotMaskMap 取结果
  • DevAscendProgramPartialUpdate 新增成员会增加结构体大小 → 已验证:编译通过,精度 PASS
  • DeviceExecuteSlot 新增 stitchCtrlBitMask 成员同上 → 已验证
  • DevLocalVector 的 entry 结构体需支持两遍模式 → 已验证
  • fullcover slot 设 slotIndex 为正值后影响 slotIndex < 0 判断 → 已解决:MarkPartialUpdateSlots 用 isPartial 区分
  • 两阶段时机约束:BuildSlotMaskMap 必须在编码循环后 → 已解决:在 RunEncodeStage 末尾调用
  • 原地压缩安全性:RefilterSlotLists 前向双指针 → 已验证
  • size-only pass:slotMaskMap == nullptr → 已解决:InitUpdateSlot 中 if (slotMaskMap == nullptr) { return; }

方案5:合并 UpdateSlotsImpl + FastStitch 的 outcast 遍历(待定)

待定:后续考虑减少重复 cell match 的开销时再评估。涉及调用时机变更,需单独分析 CallRootFunctionAlloc 与 CallRootFunctionStitch 之间 cell match 数据的依赖关系。


5. 优先级

优先级 列表 无效遍历消除 时机 实测消除率
高 updateIncastSlotList fwd 6912 / bwd 23104 encode(阶段二 Refilter) fwd -100% / bwd -97%
高 stitchIncastSlotList fwd 4536 / bwd 22042 encode(阶段二 Refilter) fwd -100% / bwd -96.9%
中 stitchOutCastSlotList fwd 2772 / bwd 2394 encode(阶段二 Refilter) -100%
低 updateOutCastSlotList 0(统一遍历,消除嵌套) encode(阶段一保守填充) —

4 个列表在阶段一保守填充(全集),阶段二 RefilterSlotLists 过滤(除 updateOutCastSlotList 全集不变)。


6. 对比命令

# fwd
rm -f /home/developer/ascend/log/debug/device-0/device-*.log
export PTO_TILE_LIB_CODE_PATH="/mnt/workspace/gitCode/cann/pto-isa"
export TILE_FWK_DEVICE_ID=0
python3 pypto-gym/custom/gdr_fwd/test_gdr_fwd.py --case varlen_T32K_H8

LATEST=$(ls -t /home/developer/ascend/log/debug/device-0/device-*.log | head -1)
grep "UpdateSlotsImpl FINAL" "$LATEST" | tail -1
grep "FastStitchConsumer stats" "$LATEST" | tail -1
grep "FastStitchProducer stats" "$LATEST" | tail -1
grep "newFuncKey" "$LATEST" | head -5

# bwd
rm -f /home/developer/ascend/log/debug/device-0/device-*.log
python3 pypto-gym/custom/gdr_bwd/test_gdr_bwd.py --case varlen64_t32k_h8_bt64

LATEST=$(ls -t /home/developer/ascend/log/debug/device-0/device-*.log | head -1)
grep "UpdateSlotsImpl FINAL" "$LATEST" | tail -1
grep "FastStitchConsumer stats" "$LATEST" | tail -1
grep "FastStitchProducer stats" "$LATEST" | tail -1
grep "newFuncKey" "$LATEST" | head -5

7. 优化后验证方法

步骤1:回退统计日志

当前 device_slot_context.cpp 和 device_stitch_context.cpp 中有统计用的 DEV_ERROR + static volatile 变量。实现预计算 slot 列表优化前需先回退这些统计日志:

git checkout -- framework/src/machine/device/dynamic/context/device_slot_context.cpp
git checkout -- framework/src/machine/device/dynamic/context/device_stitch_context.cpp

步骤2:实现预计算 slot 列表

按第 4 节方案实现 4 个列表的 encode + device 侧消费改造。

步骤3:加回统计日志验证

在优化后的代码中加回统计日志(参照本文档第 2-3 节的统计点),编译安装后单轮测试:

rm -f /home/developer/ascend/log/debug/device-0/device-*.log
export PTO_TILE_LIB_CODE_PATH="/mnt/workspace/gitCode/cann/pto-isa"
export TILE_FWK_DEVICE_ID=0
python3 pypto-gym/custom/gdr_fwd/test_gdr_fwd.py --case varlen_T32K_H8

LATEST=$(ls -t /home/developer/ascend/log/debug/device-0/device-*.log | head -1)
grep "UpdateSlotsImpl FINAL" "$LATEST" | tail -1
grep "FastStitchConsumer stats" "$LATEST" | tail -1
grep "FastStitchProducer stats" "$LATEST" | tail -1

验证标准

fwd

指标 优化前(基线) 优化后(预期)
UpdateSlotsImpl inTotal 6912 0(updateIncastSlotList 为空或仅含有效 RAW slot)
FastStitchConsumer total 6804 2268(仅含 outcast 重叠的 incast slot)
FastStitchProducer total 3339 565(仅含 partial+RAW|WAW 的 outcast slot)
无效遍历总计 14220 0

bwd

指标 优化前(基线) 优化后(预期)
UpdateSlotsImpl inTotal 23808 704(仅含 partial+RAW 的 incast slot)
FastStitchConsumer total 23444 2106(仅含 outcast 重叠的 incast slot)
FastStitchProducer total 9421 7022(仅含 partial+RAW|WAW 的 outcast slot)
无效遍历总计 47540 0

步骤4:性能对比

回退统计日志后,用多轮累积差分法做性能对比(见 SKILL.md 优化前后对比测试完整流程),对比优化前后的 STAGE_STITCH / FAST_STITCH / UPDATE_SLOT 指标。


8. 优化后实测验证报告(2026-08-10)

commit: b6a25c9ea(prefilter slot lists at encode time)
采集方法:单轮测试,device 日志 DEV_ERROR 统计(与第 2-3 节基线同方法)
基线对照:commit2 43edc032a(第 2-3 节数据)

8.1 遍历统计对比 — fwd(varlen_T32K_H8)

函数.指标 优化前 优化后 变化 说明
UpdateSlotsImpl
outTotal 3392 3392 不变 updateOutCastSlotList 全集,不筛选
├ outPartialWarWaw 576 576 不变 partial + WAR|WAW
├ outFullCoverWarWaw 2816 2816 不变 fullcover + WAR|WAW
└ outSkip (mask=NONE) — 0 新增 全集列表中无 NONE slot
inTotal 6912 0 -100% updateIncastSlotList 按 isPartial && RAW 筛选,fwd 无 RAW slot
└ inProcess 0 0 不变
FastStitchConsumer
total 6804 2268 -66.7% stitchIncastSlotList 按 WAR 筛选
├ skipInvalidIdx 4536 0 -100% encode 筛选消除
└ process 2268 2268 不变 有效遍历不变
FastStitchProducer
total 3339 567 -83.0% stitchOutCastSlotList 按 `isPartial && (RAW
├ skipNonPartial 2772 0 -100% encode 筛选消除
└ process 565 565 不变 有效遍历不变
无效遍历总计 14220 0 -100% 全部清零

8.2 遍历统计对比 — bwd(varlen64_t32k_h8_bt64)

函数.指标 优化前 优化后 变化 说明
UpdateSlotsImpl
outTotal 9568 9568 不变 updateOutCastSlotList 全集,不筛选
├ outPartialWarWaw 7136 7136 不变 partial + WAR|WAW
├ outFullCoverWarWaw 2432 2432 不变 fullcover + WAR|WAW
└ outSkip — 0 新增
inTotal 23808 704 -97.0% updateIncastSlotList 按 isPartial && RAW 筛选
└ inProcess 704 704 不变 有效遍历不变
FastStitchConsumer
total 23444 2088 -91.1% stitchIncastSlotList 按 WAR 筛选
├ skipInvalidIdx 22042 686 -96.9% encode 筛选消除大部分,残留 686 为运行时 stitchDupIdx == INVALID
└ process 1402 1402 不变 有效遍历不变
FastStitchProducer
total 9421 7027 -25.4% stitchOutCastSlotList 按 `isPartial && (RAW
├ skipNonPartial 2394 0 -100% encode 筛选消除
└ process 7022 7022 不变 有效遍历不变
无效遍历总计 47540 686 -98.6% 残留 686 为运行时状态,encode 无法预计算

8.3 残留无效遍历分析

bwd FastStitchConsumer 残留 686 次 skipInvalidIdx:这些 slot 有 WAR mask(encode 阶段合理加入 stitchIncastSlotList),但运行时 stitchDupIdx == INVALID_STITCH_IDX(前驱 rootfunc 未执行,未在 UpdateSlotsImpl 中设置 stitchDupIdx)。这是运行时控制流状态,encode 阶段无法预计算——只有当 devNextIdx == 0(首个 rootfunc 无前驱)或控制流跳过某些 rootfunc 时才会出现。

8.4 有效遍历不变性验证

所有 process / inProcess 计数优化前后完全一致,证明预计算列表正确包含了所有需要处理的 slot,没有遗漏或多余。

用例 UpdateSlotsImpl inProcess FastStitchConsumer process FastStitchProducer process
fwd 优化前 0 2268 565
fwd 优化后 0 2268 565
bwd 优化前 704 1402 7022
bwd 优化后 704 1402 7022

8.5 性能对比

8.5.0 commit2 stitch attr 优化对比(commit1 → commit2,11 轮 skip 1,us)

baseline: 078a5a738(dump only,无优化)
optimized: 43edc032a(stitch attr access 优化)
采集方法:commit 拆分模式,11 轮同进程,忽略首轮,后 10 轮差分均值。两轮采集(先基线再优化 / 先优化再基线),结果一致可复现。

fwd — varlen_T32K_H8
指标 BASELINE(commit1) OPTIMIZED(commit2) 变化 轮2变化
STAGE_STITCH 1690 1401 -17.1% -16.7%
FAST_STITCH 1100 971 -11.7% -11.0%
UPDATE_SLOT 568 409 -28.0% -28.0%
DEV_TASK_BUILD total 2446 2149 -12.1% -11.7%
DEV_TASK_BUILD first 355 316 -10.9% -8.4%
bwd — varlen64_t32k_h8_bt64
指标 BASELINE(commit1) OPTIMIZED(commit2) 变化 轮2变化
STAGE_STITCH 12411 10057 -19.0% -13.1%
FAST_STITCH 8060 6552 -18.7% -7.3%
UPDATE_SLOT 4134 3308 -20.0% -23.0%
DEV_TASK_BUILD total 18426 17662 -4.1% -1.8%
DEV_TASK_BUILD first 378 360 -4.8% -5.1%
commit2 优化特性清单(43edc032a)
  • Optimize GetTensorOffsetAndValidShape: replace 3x GetOperationAttr with single GetSymoffset + cachedAttrBase
  • Use if constexpr for skipExpression branch elimination
  • Merge CheckOffsetAndValidShapeInRawShape two loops into one
  • Remove unused shapeAttrIdx field, compute from offsetAttrIdx + dim on demand
  • Precompute offsetAttrIdx at encode time
  • Add const overload for GetOpAttrOffsetAddr
  • Move operationIndex to last param (cold path) to reduce register pressure
  • Add unlikely(clamped) to isolate DumpCellMatchAccessRange cold path
  • Isolate DumpConsumerCellAccess/DumpProducerCellAccess with DEV_IF_NONDEVICE
  • Replace wrapTaskHubOpIdx with stitchOpIdx: precompute at encode time
  • Remove IsCellMatchDescFillReady redundant check
  • Remove skipExpression template param (always false, dead code elimination)
  • Change GetTensorOffsetAndValidShape to inline void
  • Extract dims to local var in CellMatchHandle
  • Extract maxCount out of CellMatchHandleFillEnhanceExec loop
  • Unify Process signature: pass cellMatchTableDesc from CellMatchProcessByDim
  • Merge updateFuncIdx + operationIdx into taskId via MakeTaskID
  • Merge PrepareRuntimeDynamicPartialUpdateTables into UpdateSlotsImpl main loop
  • Isolate DEV_ASSERT_MSG with DEV_IF_NONDEVICE
  • Add unlikely(MaybeHaveAtomic)
  • Keep attrList view in DevAscendOperation
commit2 结论
  • 两轮结果一致,优化效果稳定可复现
  • UPDATE_SLOT 改善最大(fwd -28%、bwd -20%~23%):合并遍历 + assert 隔离 + unlikely(atomic)
  • STAGE_STITCH 显著改善(fwd -17%、bwd -13%~19%):cachedAttrBase + stitchOpIdx + CellMatchHandle 优化
  • FAST_STITCH 显著改善(fwd -11%、bwd -7%~19%)
  • DEV_TASK_BUILD total:fwd -12%,bwd -2%~4%

8.5.1 prefilter 单独优化对比(commit2 → prefilter,12 轮 skip 2,us)

baseline: commit2 43edc032a(stitch attr 优化后,prefilter 前)
optimized: prefilter 92a36b4fe(在 commit2 基础上加 prefilter slot lists)
采集方法:多轮累积差分法,parse_perf_cumulative.py --skip 2

fwd — varlen_T32K_H8
指标 BASELINE(commit2) OPTIMIZED(prefilter) 变化
STAGE_STITCH 1397 1346 -3.7%
FAST_STITCH 965 915 -5.2%
UPDATE_SLOT 410 407 -1.0%
bwd — varlen64_t32k_h8_bt64
指标 BASELINE(commit2) OPTIMIZED(prefilter) 变化
STAGE_STITCH 10083 10031 -0.5%
FAST_STITCH 6593 6645 +0.8%
UPDATE_SLOT 3293 3159 -4.1%
prefilter 三轮汇总
指标 轮1(优化先,skip1) 轮2(基线先,skip1) 轮3(基线先,skip2) 趋势
fwd STAGE_STITCH -5.3% -2.8% -3.7% 稳定改善 -3%~5%
fwd FAST_STITCH -7.8% -4.5% -5.2% 稳定改善 -5%~8%
fwd UPDATE_SLOT -0.4% +0.3% -1.0% 持平
bwd STAGE_STITCH +0.5% -2.8% -0.5% 噪声范围内
bwd FAST_STITCH +1.7% -0.0% +0.8% 持平~略差
bwd UPDATE_SLOT -3.1% -9.0% -4.1% 稳定改善 -3%~9%

8.5.2 全部优化点 vs 优化前基线(commit1 → commit2+prefilter,12 轮 skip 2,us)

baseline: 078a5a738(dump only,无任何优化)
optimized: 92a36b4fe(commit2 stitch attr 优化 + prefilter slot lists,全部优化点)
采集方法:两轮交叉验证(先基线再优化 / 先优化再基线)

fwd — varlen_T32K_H8
指标 BASELINE(commit1) OPTIMIZED(全部) 轮1变化(基线先) 轮2变化(优化先)
STAGE_STITCH 1679 1299 -22.6% -21.3%
FAST_STITCH 1090 891 -18.3% -16.0%
UPDATE_SLOT 567 383 -32.5% -33.1%
DEV_TASK_BUILD total 2430 2033 -16.3% -14.9%
DEV_TASK_BUILD first 344 294 -14.8% -11.9%
bwd — varlen64_t32k_h8_bt64
指标 BASELINE(commit1) OPTIMIZED(全部) 轮1变化(基线先) 轮2变化(优化先)
STAGE_STITCH 11862 9768 -17.7% -23.2%
FAST_STITCH 7412 6426 -13.3% -21.8%
UPDATE_SLOT 4227 3112 -26.4% -27.3%
DEV_TASK_BUILD total 17851 17929 +0.4% -6.0%
DEV_TASK_BUILD first 370 353 -4.7% -6.5%
优化点贡献分解
优化点 所属 commit fwd 影响 bwd 影响
cachedAttrBase + stitchOpIdx + CellMatchHandle + if constexpr commit2 43edc032a STAGE_STITCH -17%~19%、FAST_STITCH -11%~12% STAGE_STITCH -13%~19%、FAST_STITCH -7%~19%
合并遍历 + assert 隔离 + unlikely(atomic) commit2 43edc032a UPDATE_SLOT -28% UPDATE_SLOT -20%~23%
prefilter slot lists(消除无效遍历) prefilter 92a36b4fe STAGE_STITCH -3%~5%、FAST_STITCH -5%~8% UPDATE_SLOT -3%~9%
合计 两个 commit STAGE_STITCH -22%、FAST_STITCH -18%、UPDATE_SLOT -33% STAGE_STITCH -18%~23%、FAST_STITCH -13%~22%、UPDATE_SLOT -27%

8.6 结论

  1. 遍历统计:无效遍历 fwd 14220→0(-100%)、bwd 47540→686(-98.6%),与第 7 节预期一致(bwd 残留 686 为运行时 stitchDupIdx == INVALID,encode 无法预计算)
  2. 有效遍历不变:所有 process 计数完全一致,证明预计算列表正确性
  3. prefilter 单独性能:fwd 稳定改善(STAGE_STITCH -3%~5%、FAST_STITCH -5%~8%),bwd UPDATE_SLOT 稳定改善(-3%~9%),bwd STAGE_STITCH/FAST_STITCH 无改善(主要无效遍历已消除但耗时占比小/残留运行时 skip)
  4. 全部优化点合计性能:fwd STAGE_STITCH -22%、FAST_STITCH -18%、UPDATE_SLOT -33%;bwd STAGE_STITCH -18%~23%、FAST_STITCH -13%~22%、UPDATE_SLOT -27%。两轮交叉验证方向一致,效果稳定可复现
  5. bwd FAST_STITCH 在 prefilter 单独对比中无改善,但在全部优化点对比中改善 -13%~22%:因为 commit2 的 cachedAttrBase/stitchOpIdx 等优化贡献了主要改善,prefilter 在 bwd 中主要贡献是 UPDATE_SLOT
likedislike
GgaoxingwangCollaborators
Aug 21 Associated pull request
gaoxingwangCollaborators
Aug 22 Comments:

pr 5588 优化:
优化概述
将 task 级字段从 DynFuncData 移至 DynFuncHeader(每 task 一次赋值,而非每 func),缩小 per-func 结构体大小;隔离 debug-only 和 host-only 代码路径。

优化点

  1. Task 级字段上移
    startArgs / stackWorkSpaceAddr / stackWorkSpaceSize 从 DynFuncData 移至 DynFuncHeader
    FillOneDynFuncData 移除 startArgs / workspace 参数,在 BuildDynFuncData 中一次性赋值到 header
    Reloc/backup/restore 在 header 层执行(每 task 一次,非每 func)
  2. Debug-only 代码隔离
    CheckAddrAligned 和 leafFunc 统计包裹在 DEV_IF_DEBUG 中
    host-only 字段(exprNum / opAttrSize / rawTensorAddrSize / rawTensorDescSize)包裹在 DEV_IF_NONDEVICE 中
  3. 局部变量提取
    dupFunc.GetSource() 提取为局部变量 source(13 次调用 → 1 次)
likedislike
袁野袁野Collaborators
Aug 27 changed custom category from 任务 to 需求
6 event messages are collapsed here。 See more
LLordOfMysteriesCollaborators
Sep 11 Associated pull request
LordOfMysteriesCollaborators
Sep 11 Comments:

image.png

likedislike
LLordOfMysteriesCollaborators
Sep 12 Associated pull request
LLordOfMysteriesCollaborators
Sep 14 changed custom state from 待办的 to 已确认
LLordOfMysteriesCollaborators
Sep 14 changed custom state from 已确认 to 已解决
LLordOfMysteriesCollaborators
Sep 14 Close issue