当前位置: 首页 > news >正文

GE 和 Runtime:不是上下游,是协同决策

你以为 GE 做完融合决策,交给 Runtime 执行就行了?其实它们是一个协同系统——GE 决定"融什么",Runtime 决定"怎么跑",但 GE 的融合决策必须考虑 Runtime 的调度约束,Runtime 的调度策略也必须参考 GE 的融合结果。

这一篇把 GE 和 Runtime 的协同工作机制拆开来,说说四个被误解的设计决策。

GE 不是"图优化器",是"融合决策引擎"

很多人以为 GE(Graph Engine)就是做图优化的——算子融合、内存优化、计算图重写。这些都没错,但不是 GE 的核心。

GE 的核心是融合决策——根据算子的 shape、dtype、tiling 参数,决定哪些算子可以融合、以什么顺序融合、融合后的算子怎么调度。这个决策过程不是固定的,是可学习的——你可以读 GE 的融合规则,甚至写自定义的融合 pass。

# GE 的融合决策过程(简化版)# 来源:ge/frontend/fusion_pass/flash_attention_fusion_pass.cc# 决策1:输入 dtype 检查(必须是 float16)# if (input_dtype != DT_FLOAT16) return false;# 决策2:seq_len 检查(必须是 2 的幂次方)# int seq_len = input_shape[2];# if ((seq_len & (seq_len - 1)) != 0) return false;# 决策3:Q、K、V 的 seq_len 必须相同# if (q_shape[2] != k_shape[2] || q_shape[2] != v_shape[2]) return false;# 决策4:必须开启 causal mask(训练场景)# if (!is_causal) return false;# 验证:查看 GE 的融合决策日志importos os.environ["ASCEND_GLOBAL_LOG_LEVEL"]="3"os.environ["GE_LOG_TO_STDOUT"]="1"importtorch Q=torch.randn(4,32,2048,64,dtype=torch.float16).npu()K=torch.randn(4,32,2048,64,dtype=torch.float16).npu()V=torch.randn(4,32,2048,64,dtype=torch.float16).npu()output=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()# 在日志输出中搜索 "FlashAttentionFusionPass"# 如果看到 "FlashAttentionFusionPass: success",说明 GE 的融合决策成功了

误解:GE 是图优化器,做的事情就是算子融合、内存优化。
纠正:GE 的核心是融合决策——根据算子的 shape/dtype/tiling,决定哪些算子可以融合、以什么顺序融合。这个决策过程是可学习的。

Runtime 不是"任务调度器",是"overlap 协调器"

很多人以为 Runtime 就是调度算子执行的——哪个算子先执行、哪个后执行、哪些可以并行。这些都没错,但不是 Runtime 的核心。

Runtime 的核心是overlap——让数据搬运和计算重叠起来。具体来说:当前 tile 的计算在进行的时候,Runtime 已经把下一个 tile 的数据从 HBM 搬到 UB 上了。这样计算单元就不会停下来等数据。

# Runtime 的 overlap 机制(简化版)# 来源:runtime/core/mem_manager/overlap_manager.cc# overlap 的核心逻辑:# 1. 把算子按 tile 切分# 2. 当前 tile 在计算的时候,预取下一个 tile 的数据# 3. 计算完当前 tile,立刻开始计算下一个 tile(数据已经就位)# 验证:用 Profiler 抓 trace,看计算 kernel 和数据搬运 kernel 的时间轴fromtorch_npu.profilerimportprofile,ProfilerActivity Q=torch.randn(4,32,4096,64,dtype=torch.float16).npu()K=torch.randn(4,32,4096,64,dtype=torch.float16).npu()V=torch.randn(4,32,4096,64,dtype=torch.float16).npu()withprofile(activities=[ProfilerActivity.NPU],export_name="runtime_overlap.json"):output=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()# 分析 runtime_overlap.json:# - 如果计算 kernel(FlashAttentionKernel)和数据搬运 kernel(MemcpyH2D)有重叠# → Runtime 的 overlap 生效了 ✅# - 如果计算 kernel 和数据搬运 kernel 完全串行# → Runtime 的 overlap 未生效 ❌# 对比 overlap 开启/关闭的性能os.environ["ASCEND_OVERLAP_DISABLE"]="1"# 关闭 overlaptorch.npu.synchronize()start=time.time()for_inrange(50):output=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()end=time.time()print(f"overlap 关闭后 50 次耗时:{end-start:.2f}s")os.environ["ASCEND_OVERLAP_DISABLE"]="0"# 开启 overlaptorch.npu.synchronize()start=time.time()for_inrange(50):output=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()end=time.time()print(f"overlap 开启后 50 次耗时:{end-start:.2f}s")

误解:Runtime 是任务调度器,决定算子执行顺序。
纠正:Runtime 的核心是 overlap——让数据搬运和计算并行,减少计算单元等待数据的时间。

GE 和 Runtime 不是"上下游",是"协同决策"

很多人以为 GE 做完融合决策,生成融合后的算子,交给 Runtime 执行就行了。这个理解太浅了。

GE 的融合决策必须考虑 Runtime 的调度约束。比如:如果一个算子融合后太大(tile 数太多),Runtime 可能无法有效地做 overlap(因为内存不够存两个 tile 的数据)。GE 在决策融合的时候,必须参考 Runtime 的 overlap 可行性。

反过来,Runtime 的调度策略也必须参考 GE 的融合结果。比如:融合后的算子更适合 tile 级 pipeline(因为一个融合算子内部可以切分 tile),Runtime 会根据融合算子的特性调整调度策略。

# GE 和 Runtime 的协同决策(简化版)# 场景:GE 在决策是否为 FlashAttention 做融合时,会参考 Runtime 的 overlap 可行性# GE 的考虑:# 1. 融合后的 FlashAttentionKernel 有多大?(tile 数 × 每个 tile 的 UB 占用)# 2. Runtime 能不能有效地做 overlap?(UB 够不够存两个 tile 的数据)# 3. 如果 UB 不够,GE 可能不会触发融合,或者选择一个更小的 tile 大小# Runtime 的考虑:# 1. 这个算子是不是融合算子?(融合算子更适合 tile 级 pipeline)# 2. 融合算子的 tile 大小是多少?(决定预取策略)# 3. 根据融合算子的特性,调整调度策略(比如更多的 pipeline 级)# 验证:对比不同 tile 大小下,GE 是否触发融合 + Runtime 的 overlap 效率importtorchimporttimefortile_sizein[64,128,256,512]:Q=torch.randn(4,32,4096,tile_size,dtype=torch.float16).npu()K=torch.randn(4,32,4096,tile_size,dtype=torch.float16).npu()V=torch.randn(4,32,4096,tile_size,dtype=torch.float16).npu()# 查看 GE 日志,看这个 tile_size 下是否触发了 FlashAttentionFusionoutput=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()# 计时start=time.time()for_inrange(100):output=torch.nn.functional.scaled_dot_product_attention(Q,K,V,is_causal=True)torch.npu.synchronize()end=time.time()print(f"tile_size={tile_size}, 100次耗时:{end-start:.2f}s")# 用 npu-smi 查看 Compute Cube 利用率# 如果利用率 > 80%,说明 Runtime 的 overlap 做得好(计算单元没怎么等数据)# 如果利用率 < 50%,说明计算单元经常在等数据(overlap 没生效或 tile 大小不合适)

误解:GE 和 Runtime 是上下游关系——GE 做完决策,Runtime 执行就行。
纠正:GE 和 Runtime 是协同决策关系——GE 的融合决策要考虑 Runtime 的调度约束,Runtime 的调度策略要参考 GE 的融合结果。

ops-transformer 不是"被动适应",是"主动配合"

很多人以为 ops-transformer 的算子只要实现功能就行,GE 和 Runtime 会自动优化。这个理解是错的。

ops-transformer 的算子设计必须主动配合GE 的融合规则和 Runtime 的调度策略。具体来说:

  1. 暴露 tiling 参数:让 GE 在决策融合的时候,知道这个算子支持什么样的 tile 大小
  2. 支持 causal mask:让 GE 在匹配 FlashAttentionFusionPass 的时候,知道这个算子支持 causal mask
  3. 优化 UB 使用:让 Runtime 在做 overlap 的时候,有足够的内存预取下一个 tile 的数据
# ops-transformer 的算子设计(主动配合 GE 和 Runtime)# 来源:ops-transformer/src/ops_transformer/flash_attention/flash_attention_kernel.cpp# 主动配合1:暴露 tiling 参数(让 GE 知道这个算子支持什么 tile 大小)# void FlashAttentionKernel(..., int tiling) { ... }# 主动配合2:支持 causal mask(让 GE 匹配 FlashAttentionFusionPass)# if (causal) { ... // 在 softmax 之前把 mask 位置设为 -inf ... }# 主动配合3:优化 UB 使用(让 Runtime 有足够的空间做 overlap)# - 每个 tile 的 UB 占用尽量小# - 预留足够的 UB 空间给下一个 tile 的数据预取# 验证:读 ops-transformer 的源码,看它是怎么主动配合 GE 和 Runtime 的importsubprocess# 查看 tiling 参数的定义result=subprocess.run(["grep","-r","tiling","ops-transformer/src/ops_transformer/flash_attention/"],capture_output=True,text=True)print("tiling 参数定义:")print(result.stdout)# 查看 causal mask 的实现result=subprocess.run(["grep","-r","causal","ops-transformer/src/ops_transformer/flash_attention/"],capture_output=True,text=True)print("causal mask 实现:")print(result.stdout)# 查看 UB 使用的优化result=subprocess.run(["grep","-r","UB","ops-transformer/src/ops_transformer/flash_attention/"],capture_output=True,text=True)print("UB 使用优化:")print(result.stdout)

误解:ops-transformer 的算子只要实现功能就行,GE 和 Runtime 会自动优化。
纠正:ops-transformer 的算子设计必须主动配合 GE 和 Runtime——暴露 tiling 参数、支持 causal mask、优化 UB 使用。

相关仓库:

https://atomgit.com/cann/ops-transformer

https://atomgit.com/cann/ge

https://atomgit.com/cann/runtime

http://www.jsqmd.com/news/875514/

相关文章:

  • Midjourney --style raw + 调色板协同失效?3步诊断流程+4类硬件级色彩配置冲突解决方案
  • 反应坐标映射:非马尔可夫开放量子系统的高效模拟方法
  • B物理反常的全局拟合:有效场论与机器学习解析新物理信号
  • 神经材质:NeRF之后,下一代数字内容的“皮肤”革命
  • Harness Engineering:麻绳还是马绳
  • SVM在频繁模式挖掘中的应用:从高维稀疏数据中提取判别性关联规则
  • Leslie矩阵建模:从种群动力学到捕食竞争与机器学习拟合
  • 从《原神》到《黑神话》都在用的AI Agent中间件:轻量级推理框架v0.9.3内部测试版首次泄露(仅限前500名开发者)
  • 别急着重启!深入理解Ubuntu 22.04的needrestart:守护进程、库文件与系统更新背后的原理
  • Telnet与SSH协议安全本质对比:从明文传输到公钥认证
  • 神经阴影:当AI学会“画影子”,实时渲染的下一个突破口
  • KNO标度律与粒子多重数:从QCD喷注结构到夸克-胶子鉴别的理论推导
  • 从语义网到神经符号系统:知识图谱与LLM融合实战指南
  • 为什么你的MJ图总像“老胶片过曝”?揭秘ISO模拟算法缺陷,5种降颗粒参数组合实测对比(含LUT映射表)
  • Spark Transformer:稀疏激活优化与计算效率提升
  • 别再手动处理表格了!用PyQt6的QTableWidget自定义右键菜单,5分钟搞定复制粘贴与格式设置
  • 基于共享潜在空间的贝叶斯优化:解决异构算法超参数联合选择难题
  • ml_edm:基于成本敏感的时间序列早期分类Python工具包详解
  • Node.js版Frida实战指南:告别Python环境陷阱
  • 软共线因子化与IRC安全:从QCD发散到喷注算法的物理基础
  • 傅里叶变换与FFT:从信号处理到深度学习卷积加速的工程实践
  • 端侧智能与多模态传感:OmniBuds平台如何重塑下一代智能耳戴设备
  • 从DALL·E 3到Midjourney 6:对比度渲染引擎差异白皮书(附17组跨模型PSNR/SSIM实测数据)
  • 开源机器学习项目贡献者角色演化与社区健康度分析
  • 量子贝叶斯网络在环境监测中的应用:解决数据不平衡的油污检测
  • 虚幻引擎程序化体积云渲染:告别天气纹理,实现动态天空
  • Agent 状态持久化:基于 Redis 的多轮交互上下文存储方案
  • 统信UOS 20.1060专业版美化全攻略:从桌面到GRUB再到锁屏,一次搞定个性化设置
  • 车企AI Agent团队组建白皮书(附2024头部厂商组织架构图+7个核心岗位能力雷达图)
  • R语言实现Heston模型COS期权定价:从傅里叶变换到高效数值计算