想象你刚买了一套毛坯房——几十面墙隔出一个个小房间,每进一个房间都要掏钥匙开门(GPU上每运行一个算子都要启动一次 kernel),动线绕得人头晕。房产中介介绍了一位装修队长(AI 编译器),他提出一堆改造方案:把客厅和餐厅的隔墙打通,动线更顺畅(算子融合);把堆杂物的小储藏室直接封掉(死代码消除);把家具在工厂就拼好再搬进来(常量折叠)。 问题来了:每一面墙到底值不值得拆?拆完体感能提升多少?装修费时、占地方,会耽误入住吗?多个项目一起做,是 1+1=2 还是 1+1>2?本文就像一个精明的业主,带你系统学会“图优化的效果分析”:先定三把尺子,再逐个量单项改造,接着看组合效果,最后用一张成本收益表选出最优装修包,并在 PyTorch 2.0 的 torch.compile 中实操一遍这套方法论。

岗位关联

掌握优化效果分析,你就能在模型部署时用数据说话:科学选择编译策略,既拿到推理性能提升,又不被编译成本拖垮。这是 AI 编译与推理优化工程师的核心竞争力。

目录
  1. 可视化演示
  2. 优化目标定义
  3. 单Pass效果评估
  4. 组合优化分析
  5. 加速比测量方法
  6. 内存优化评估
  7. 编译开销权衡
  8. 配置选择策略
  9. PyTorch编译实战
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

优化目标定义

装修前,业主必须想清楚一个问题:什么叫“装修成功”?有人在意住得宽敞(省内存),有人在意行动利索(低延迟),有人在意工期短(编译快)。答案不同,后面每一项决策都会不同。图优化也是如此,第一步不是选 Pass,而是定义三把尺子。

三把尺子分别是:加速比、峰值内存、编译开销。加速比是核心指标,它回答“优化后到底快了多少”:

除了速度,另外两把尺子同样不可偏废:峰值内存是运行期间某个瞬间占用的最大内存量——装修不能把楼道都堆满;编译开销是跑一遍 Pass 本身花的时间与内存——装修队的人工和场地费。三把尺子定好,面对装修队的几十张方案,你才不会被一句“这个优化特别好”牵着走。

S=TbaseTopt\displaystyle S = \frac{T_{\text{base}}}{T_{\text{opt}}}
加速比 S = 没优化时的运行时间 ÷ 优化后的运行时间。S=2.0 就意味着推理快了一倍。
  1. 分子 T_base未经过任何图优化的原始执行时间,它是一切比较的基准线,相当于毛坯房的原始动线耗时。
    Tbase\displaystyle T_{\text{base}}
  2. 分母 T_opt经过一个或多个 Pass 优化后的执行时间,对应于装修后的入住体验。
    Topt\displaystyle T_{\text{opt}}
  3. 商 S两者相比得到的倍数,S 越大代表优化越有效,这是全篇最核心的度量。
    S=TbaseTopt\displaystyle S = \frac{T_{\text{base}}}{T_{\text{opt}}}

单Pass效果评估

装修队开始逐项报价。第一项是“拆墙”——算子融合。GPU 上每运行一个算子就要启动一次 kernel,频繁启动就像每过一扇门都要掏一次钥匙。把相邻的 matmul 和 add 合并成一个融合算子,只掏一次钥匙,中间张量也不写回显存了。融合的收益公式很直观:

第二项是“拆储藏室”——死代码消除(DCE)。模型里常有定义了却没人用的分支,比如调试期残留的算子,它们白白占着显存和算力。DCE 从输出节点反向扫描,凡是到不了输出的节点直接删掉,低成本甚至零成本。第三项是“预制家具”——常量折叠:像 2 * x + 1 / 0.5 这种表达式,编译器在编译期就算出答案,运行时连算都不用算。

单项测评的方法非常直白:跑一次基线,只打开这一个 Pass 再跑一次,分别算出加速比。你会发现“拆墙”收益最大,“封储藏室”次之,“预制家具”在大模型上常常毫无体感——不同 Pass 在不同模型上的收益差异,正是效果分析要迈出的第一步。

Tfused=Ta+Tb−Tio\displaystyle T_{\text{fused}} = T_a + T_b - T_{\text{io}}
融合后的时间 ≈ 两个算子各自的时间之和,再减去中间张量读写的那一趟“搬运”。省下来的就是纯利润。
  1. Ta + Tb未融合时两个算子分别执行的时间,各自独立、互不相干。
    Ta+Tb\displaystyle T_a + T_b
  2. T_io前一个算子的输出先写进显存,后一个算子再从显存读出来,这趟搬运动作的耗时。
    Tio\displaystyle T_{\text{io}}
  3. 整体相减融合后中间结果留在芯片的寄存器或缓存里,搬运时间直接归零。
    Tfused=Ta+Tb−Tio\displaystyle T_{\text{fused}} = T_a + T_b - T_{\text{io}}

组合优化分析

现实装修中,业主不会只做一项。但多项一起做,数学不是简单的 1+1=2,而是会出现三种典型情况:协同、冲突、边际递减。

协同最理想:先拆了墙(融合),杂物间(死代码)反而更容易暴露出来被 DCE 删掉——两个 Pass 互相成就。冲突最头疼:一个 Pass 重构了图结构,恰好破坏了另一个 Pass 的匹配模式,后来者有心无力。边际递减最普遍:每多一个 Pass,新增收益占已有优化的比例越来越小,就像墙拆得再多,也就那几堵。两个 Pass 串行后的总加速比,需要引入协同系数修正:

在真实编译器里,Pass 调度器(如 MLIR 的 Pass Pipeline)专门研究顺序问题。效果分析的实践价值就在于:与其拍脑袋堆 Pass,不如把 η 真正测出来——在编译管线中插入埋点,逐一记录每一步之后的图规模和运行时间,用数据确认“合则两利”还是“合则两伤”。

Sab=Sa×Sb×η\displaystyle S_{ab} = S_a \times S_b \times \eta
η 是协同系数:η>1 表示两个 Pass 互相增益,η<1 表示互相拖累,等于 1 才是理想化的独立相乘。
  1. S_a × S_b如果两个优化完全独立,理想总加速比是两者相乘,这是理论最大值。
    Sa×Sb\displaystyle S_a \times S_b
  2. 协同系数 η真实编译器中 Pass 会共享或破坏彼此的前提条件,η 用来修正理想值。
    η\displaystyle \eta
  3. 整体判断η>1 说明“合则两利”,η<1 说明要重新考虑 Pass 顺序或组合。
    Sab=Sa×Sb×η\displaystyle S_{ab} = S_a \times S_b \times \eta

加速比测量方法

业主说“住得更快”其实很含糊:是跨门槛更快,还是整趟出门更快?编译器里也有三种量法,各有用处。

端到端延迟:整个模型前向一遍的墙钟时间(wall-clock time),最接近用户体感,是最终裁决者。算子级加速:单独看某个被优化的 kernel 快了多少——能体现 Pass 的技术成色,但未必等于整体变化。吞吐量:单位时间处理多少请求,服务端批量场景最关心这个。这三种量法背后,藏着一条著名的规律——Amdahl 定律:

所以效果分析报告里,一定要同时报端到端和单算子两个数字。单算子 2 倍提升、端到端却只有 1.1 倍,多半是 Amdahl 在提醒你:瓶颈在别处。要么换一个 Pass,要么先优化那个“不可优化”的数据搬运部分。

Soverall=1(1−f)+f/Sp\displaystyle S_{\text{overall}} = \frac{1}{(1 - f) + f / S_p}
整体提速被不可优化的部分卡脖子。哪怕你把可优化部分提速到无穷大,整体加速比也顶多是 1/(1−f)。
  1. 分母左项 1−f不可优化部分在总时间中的占比,这是整体提速的硬下限。
    (1−f)\displaystyle (1 - f)
  2. 分母右项 f/S_p可优化部分被加速 S_p 倍之后占用的时间比例。
    fSp\displaystyle \frac{f}{S_p}
  3. 整体取值两部分相加后取倒数,短板决定了天花板。
    Soverall=1(1−f)+f/Sp\displaystyle S_{\text{overall}} = \frac{1}{(1 - f) + f / S_p}

内存优化评估

GPU 显存就是业主的房租。模型推理时,每个算子的中间结果都占一块地方:融合减少中间张量,DCE 删掉冗余缓存,重计算则是“以算代存”。省内存的效果,要用峰值内存对比来衡量:

除了峰值,还要盯两件事。内存复用:不同算子的生命周期不再重叠时,它们的缓冲区能不能被重复使用——好的分配器让融合后的空档恰好被下一个算子接住。碎片化:反复分配释放会产生小孔洞,显存被切得细碎,新的大张量反而放不下。长任务评估要看稳定运行后的内存水位,而不是只看第一帧。

储物空间省出来的本质,是把“每个房间都堆满杂物”改成“同一辆推车在不同工地循环使用”。占用少了,GPU 就能同一时间租给更多客户——也就是塞进更大的 batch。

ηmem=Mbase−MoptMbase×100%\displaystyle \eta_{\text{mem}} = \frac{M_{\text{base}} - M_{\text{opt}}}{M_{\text{base}}} \times 100\%
η_mem 是内存节省率。M_base 是优化前峰值内存,M_opt 是优化后峰值内存。60% 意味着省下了六成显存。
  1. 分子差值优化前后峰值内存的差,即真正省下的那部分空间。
    Mbase−Mopt\displaystyle M_{\text{base}} - M_{\text{opt}}
  2. 分母 M_base以基线为参照,计算出相对节省的比例,而不是绝对字节数。
    Mbase\displaystyle M_{\text{base}}
  3. ×100%换成直观的百分比,便于不同模型之间横向比较。
    ηmem=Mbase−MoptMbase×100%\displaystyle \eta_{\text{mem}} = \frac{M_{\text{base}} - M_{\text{opt}}}{M_{\text{base}}} \times 100\%

编译开销权衡

到这一步,业主开始算总账:装修期间不能住人,这个时间成本怎么算?编译开销由三部分组成:编译耗时、编译内存、触发编译的时机。一个 Pass 跑几秒不算什么,但如果模型每次部署都要重新编译,且上线后只服务几十个请求,那就亏大了。

量化的关键是一个盈亏平衡点:

实务上有三条经验。第一,编译缓存极其重要:PyTorch 与 XLA 的编译缓存让相同图结构二次部署直接命中,避免重复装修,成本近似归零。第二,注意触发策略:边训练边预热编译、异步编译,把装修时间藏在训练间隙里。第三,编译内存本身也是成本:编译过程把显存挤爆导致 OOM,在长上下文 LLM 推理中尤其要留意。

业主拍板的逻辑很简单:这套工期值不值,取决于房子打算住多久、每天住几次。效果分析报告必须包含这张“回本账”,否则优化方案就只是一张好看的效果图。

Nbreak-even=TcompileTbase−Trun,opt\displaystyle N_{\text{break-even}} = \frac{T_{\text{compile}}}{T_{\text{base}} - T_{\text{run,opt}}}
平衡请求次数 = 编译花的时间 ÷ 每次请求省下的时间。请求次数超过这个数,优化才回本。
  1. 分子 T_compile一次编译的总耗时,包含所有 Pass 的执行时间。
    Tcompile\displaystyle T_{\text{compile}}
  2. 分母差值每条请求从原时间变为优化后时间,单次省下的 ΔT。
    Tbase−Trun,opt\displaystyle T_{\text{base}} - T_{\text{run,opt}}
  3. 两者相除跑多少次能赚回编译成本,少于这个次数就是负收益。
    Nbreak-even=TcompileTbase−Trun,opt\displaystyle N_{\text{break-even}} = \frac{T_{\text{compile}}}{T_{\text{base}} - T_{\text{run,opt}}}

配置选择策略

报价单上有几十个项目,业主不可能全做。第一步是画成本收益矩阵:横轴是编译开销,纵轴是加速比,每个 Pass 组合对应一个点。右上角听着好但往往贵,左下角便宜却没用。真正值得选的,是落在 Pareto 前沿上的点——不存在其他方案能在不增加成本的前提下获得更高收益。

收益函数帮助量化这个选择:

实务上,启发式筛选加自动调优是黄金两步走:先用规则砍掉明显被支配的点(收益低且成本高),再在候选集合上用自动搜索(如贝叶斯优化)实测少数几个组合,选出最优。工程里还常把模型按 shape 聚类,同一类 shape 复用同一个配置,避免每个新 shape 都重新搜索。

业主最终选定的装修包,往往是“中等预算全屋套装”——不是每一项都做,但关键项目全包。这也是效果分析的最终产出:一份可解释、可复测、可迁移的配置决策报告。

U=S1+α⋅TcompileTrun\displaystyle U = \frac{S}{1 + \alpha \cdot \frac{T_{\text{compile}}}{T_{\text{run}}}}
U 是综合收益。α 是用户对编译时间的敏感系数:α 越大越在意编译速度,越该选保守配置;α=0 时只在乎推理性能。
  1. 分子 S该配置带来的加速比,收益的主体部分。
    S\displaystyle S
  2. 分母中的比值编译时间与单次运行时间的比,放大低频部署场景下的编译成本。
    TcompileTrun\displaystyle \frac{T_{\text{compile}}}{T_{\text{run}}}
  3. α 调节把编译成本按敏感度折进收益,让公式适配不同业务场景。
    1+α⋅TcompileTrun\displaystyle 1 + \alpha \cdot \frac{T_{\text{compile}}}{T_{\text{run}}}

PyTorch编译实战

带着这套方法论,来看 PyTorch 2.0 的编译栈。torch.compile 就是那位装修总包:先用 Dynamo 把 Python 代码转成计算图(画户型图),再用 Inductor 做图优化和代码生成(施工)。它提供三种编译模式,对应三档预算:

  • debug:几乎不优化,只做正确性验证,相当于跟房主确认户型图没错。
  • reduce-overhead:减少 Python 解释与 kernel 启动开销,用 CUDA Graph 把一长串指令“录制成视频一次性播放”。
  • max-autotune:全屋定制,自动搜索最优 kernel 与融合策略,编译时间最长。

选哪种模式,本质是一个总成本最小化问题:

实测步骤就是前面所有章节的综合演练:对同一个 LLM,分别用三种 mode 编译,测端到端延迟、峰值内存、显存占用;再套用第 6 节的回本公式,结合线上 QPS 估算哪种 mode 最划算。结论通常符合直觉:小模型低调用量用 reduce-overhead 最香,大模型高频请求才值得 max-autotune 的编译时间。

毛坯房终于装修完成。从定尺子、量单项、算组合、选配置到最终交付,图优化的效果分析,本质是让每一次编译决策都有据可依、有账可算。

Twall=Tcompile+Trun(M,mode)\displaystyle T_{\text{wall}} = T_{\text{compile}} + T_{\text{run}}(M, \text{mode})
墙钟时间 = 编译时间 + 运行时间。性能不能只看运行时间,mode 升级带来的编译增量可能吃掉全部收益。
  1. T_compile该模式下的编译耗时,max-autotune 最高,debug 最低。
    Tcompile\displaystyle T_{\text{compile}}
  2. T_run(M, mode)运行时间同时取决于模型 M 和模式选择,是模式比较中的自变量。
    Trun(M,mode)\displaystyle T_{\text{run}}(M, \text{mode})
  3. 总时间部署场景要比的是两者之和,而不是单独比某一项。
    Twall=Tcompile+Trun\displaystyle T_{\text{wall}} = T_{\text{compile}} + T_{\text{run}}

AI 应用场景

LLM 在线推理服务部署

阶段:模型部署与推理优化

解决的问题:传统做法是直接加载 PyTorch 模型提供服务,Python 调度开销大、kernel 启动频繁,GPU 利用率低。工程师常凭直觉全开所有优化,结果编译时间拖慢上线节奏。借助效果分析方法,可以先用三种 mode 分别编译,测端到端延迟与编译耗时,再用盈亏平衡点公式结合 QPS 决定最终模式。

具体案例:某问答服务每天约 50 万次请求,模型为 7B LLM。工程师对比 reduce-overhead 与 max-autotune 后发现:后者虽有 1.3 倍加速,但编译时间从 40 秒涨到 8 分钟,且单请求只节省 3 毫秒。按回本公式计算需要约 16 万次请求才能赚回编译成本,而服务每小时就有 2 万次请求,三天回本,值得上豪华套餐。

边缘设备上的内存受限部署

阶段:模型压缩与运行时优化

解决的问题:手机、自动驾驶芯片上的显存(或内存)只有几 GB,峰值内存超限直接导致 OOM 崩溃。传统做法是暴力降低 batch size,牺牲吞吐。通过单 Pass 内存评估,可以精准找到削减峰值内存贡献最大的 Pass 组合,并用内存复用分析验证方案可行性。

具体案例:某视觉模型在 Jetson Orin 上部署,baseline 峰值内存 3.2GB,开启算子融合后降到 2.1GB(节省 34%),再叠加死代码消除后降到 1.8GB(累计节省 44%)。整个过程只有融合+DCE 两个 Pass,避免了全量优化带来的编译时间和内存开销,最终 batch size 从 4 提升到 8。

模型发布流水线中的编译策略自动化

阶段:CI/CD 与模型版本管理

解决的问题:模型每周迭代,每次发布手动选择编译模式既慢又容易出错。传统流程里编译策略靠个人经验,换硬件后失效也无人察觉。用成本收益矩阵 + 自动调优的思路,把配置选择做成发布流水线中的一个自动化步骤。

具体案例:某推荐系统团队在 CI 中加入“编译策略评估”任务:每次提交模型后自动跑基线与三个候选配置的对比评测,输出加速比、内存节省率、编译开销三项指标,并自动选择 Pareto 前沿上性价比最高的配置写入部署清单。上线后平均推理延迟下降 28%,发布流程零人工干预。

Python 演示

python72 行
"""图优化Pass效果评估模拟:比较不同Pass组合的加速比与编译开销"""  def build_ops():    """构造计算图:一段模拟注意力前向的原子算子序列"""    return [        ("mm1", "matmul", 3.2), ("add1", "add", 0.4),        ("mm2", "matmul", 3.2), ("add2", "add", 0.4),        ("mm3", "matmul", 3.2), ("add3", "add", 0.4),        ("qk", "matmul", 2.8), ("scale", "scale", 0.5),        ("scale_dup", "scale", 0.5),        ("softmax", "softmax", 1.6), ("attn", "matmul", 2.8),        ("proj", "matmul", 3.2), ("bias", "add", 0.4),        ("dead", "add", 0.5), ("unused", "relu", 0.9),    ]  def apply_fusion(ops):    """Pass 1 算子融合:相邻 matmul+add 合并为一个融合算子"""    fused, i = [], 0    while i < len(ops):        if i + 1 < len(ops) and ops[i][1] == "matmul" and ops[i + 1][1] == "add":            cost = (ops[i][2] + ops[i + 1][2]) * 0.6            fused.append(("linear%d" % len(fused), "fused_linear", cost))            i += 2        else:            fused.append(ops[i])            i += 1    return fused  def apply_dce(ops):    """Pass 2 死代码消除:剔除名字带 dead/unused 的冗余算子"""    return [op for op in ops if not op[0].startswith(("dead", "unused"))]  def apply_cse(ops):    """Pass 3 公共子表达式消除:同类型重复算子只保留第一个"""    seen, out = set(), []    for name, kind, cost in ops:        if kind in seen:            continue        seen.add(kind)        out.append((name, kind, cost))    return out  def evaluate(ops, num_passes):    """估算加速比与编译开销:加速比按总成本折算,编译开销随Pass数增长"""    base_cost = sum(c for _, _, c in build_ops())    total = sum(c for _, _, c in ops)    speedup = base_cost / total    compile_cost = 0.5 + 0.4 * num_passes + 0.05 * len(ops)    return speedup, compile_cost  def run():    """跑通所有配置并打印对比表"""    configs = [        ("基线(无优化)", build_ops(), 0),        ("算子融合", apply_fusion(build_ops()), 1),        ("融合+死代码消除", apply_dce(apply_fusion(build_ops())), 2),        ("融合+消除+子表达式", apply_cse(apply_dce(apply_fusion(build_ops()))), 3),    ]    print("%-22s%10s%12s" % ("优化配置", "加速比", "编译开销"))    for name, ops, n in configs:        s, c = evaluate(ops, n)        print("%-22s%10.2f%12.1f" % (name, s, c))  if __name__ == "__main__":    run()
目的:模拟一个含冗余算子的计算图,依次应用三种图优化 Pass,输出不同配置下的加速比与编译开销——复现正文中“组合效果未必叠加、编译开销随 Pass 增长”的核心结论。
  1. ("linear%d" % len(fused), "fused_linear", cost)融合后生成一个新算子,名字按序号自动编号,对应装修中“打通隔墙后形成的新房间”。
  2. cost = (ops[i][2] + ops[i + 1][2]) * 0.6融合算子的开销只是两者之和的 60%,省下的 40% 就是中间张量读写的搬运费。
  3. if not op[0].startswith(("dead", "unused"))DCE 用名字前缀判断冗余节点,相当于保安检查“这个杂物间有没有人用”。
  4. if kind in seen: continueCSE 遇到同类型算子直接跳过,模拟“相同计算只做一次”。
  5. base_cost / total用基线总成本除以优化后总成本得到加速比,这就是正文里的 S 公式。
  6. 0.5 + 0.4 * num_passes + 0.05 * len(ops)编译开销 = 固定成本 + 每个 Pass 的边际成本 + 剩余算子的管理成本,体现“Pass 越多编译越贵”。
  7. ("%-22s%10.2f%12.1f" % ...)格式化输出对比表,让加速比和编译开销并列,一眼看出性价比。

常见误区

误区把图优化当叠 Buff,觉得所有 Pass 全开一定最强。反正编译器免费,多开几个总没错吧?

正解Pass 之间存在冲突和边际递减。全开可能增加编译时间,甚至一个 Pass 重写图结构后破坏另一个 Pass 的匹配前提。第 3 节的协同系数 η 说明:η<1 时合则两伤。正确做法是用成本收益矩阵选择,而不是盲目堆叠。

为何常见:单个 Pass 往往确实有效,人容易线性外推认为多个 Pass 效果叠加;编译器的黑盒属性让人难以看清内部的相互影响。

误区在 profiler 里看到某个算子融合后快了 2 倍,转头就在周报里写“模型推理提速 2 倍”。

正解Amdahl 定律揭示了真相:整体提速受不可优化部分的限制。如果被优化的算子只占总时间的 20%,就算它提速到无穷大,整体也只提升 25%。端到端测量才是最终标准,单算子加速只是线索。

为何常见:profiler 第一屏总是显示耗时最亮眼的算子,人容易把局部当全局,忽略了 IO、调度等未被优化的部分。

误区优化只赚不赔,编译时间可以忽略不计,反正后台跑完就完事了。

正解编译有显性成本和隐性成本:编译耗时、编译内存,以及部署流水线里等待编译完成的空转。小模型或短生命周期任务里,编译开销可能超过优化收益。必须用盈亏平衡点公式 N = T_compile / ΔT 算清楚回本需要的请求次数。

为何常见:编译在后台静默执行,用户只看到优化后的运行时间变短,却看不到编译阶段付出的时间与机器资源。

误区所有 Pass 都在省内存,优化完显存占用肯定下降,所以只看速度不看内存。

正解算子融合和 DCE 确实省内存,但循环展开、计算重排等策略为了提速会复制数据或扩大中间缓冲,内存可能不降反升。峰值内存必须单独测量,与加速比并列评估,不能想当然。

为何常见:人们习惯把“优化”等同于“省资源”,却忽略了速度优化有时以空间为代价——就像为了更快地搬东西,反而多买了几辆推车。

误区模型 A 上调好的 Pass 组合,换到模型 B、换张新显卡,直接复制就能用。

正解最优配置强依赖图结构、硬件型号和 batch size。比如从 A100 换到 H100,内存带宽翻倍,融合收益与编译开销的权衡点完全改变。每次升级硬件或大改模型后,都应该重新跑一遍效果分析。

为何常见:工程上希望复用经验、少跑实验,但图优化的强“模型相关性”和“硬件相关性”,让这种偷懒频频在线上踩坑。

面试题

Q1. 加速比 S 的定义中,作为比较基准的 T_base 指的是什么?

单选题 · easy

  • A.最新 GPU 上的理论峰值时间
  • B.未优化图编译后的执行时间← 正确答案
  • C.单个算子最短执行时间之和
  • D.优化后图在 CPU 上的时间

答案:B。加速比衡量的是“优化”带来的收益,所以基准必须是同一份模型在未经过图优化时的执行时间(对应毛坯房的原始动线耗时)。A 是理论极限不可作为实际基准;C 忽略了算子间的依赖与调度开销;D 换了硬件平台,对照不公平。

Q2. 下列哪些 Pass 组合通常会降低峰值内存?

多选题 · mid

  • A.算子融合减少中间张量← 正确答案
  • B.死代码消除删冗余节点← 正确答案
  • C.常量折叠预计算表达式← 正确答案
  • D.循环展开增加指令并行

答案:ABC。算子融合省去了中间张量的显存读写;死代码消除直接删掉占空间的冗余节点;常量折叠把运行时计算挪到编译期,也减少临时缓冲。循环展开通常为了提升指令级并行,可能复制数据从而增大内存占用,不能一概认为省内存。

Q3. 为什么两个 Pass 串行应用的总加速比往往不等于各自加速比的简单相加?

单选题 · mid

  • A.算子并行度会随编译变化
  • B.不同 Pass 可能冲突或互相增益← 正确答案
  • C.编译器版本不同差异较大
  • D.模型数据量会动态发生改变

答案:B。Pass 之间可能产生协同(η>1)或冲突(η<1),如融合后暴露出更多死代码,而有些 Pass 会破坏彼此的匹配模式。这就像装修中拆墙后更容易封杂物间,但也可能拆错墙导致另一项工程返工。A、C、D 都不是“总加速比偏离简单相加”的根本原因。

Q4. 以下哪个场景中,编译开销最可能吃掉优化收益?

单选题 · easy

  • A.大模型长时运行的在线服务
  • B.小模型但调用次数很少的任务← 正确答案
  • C.批量推理吞吐量饱和的集群
  • D.编译缓存命中率很高的部署

答案:B。用回本公式 N = T_compile / ΔT 判断:小模型单次节省的时间 ΔT 很小,调用次数又少,需要很多次请求才能攒够编译成本。相反,长时运行的在线服务请求量大,很容易回本;缓存命中率高说明编译成本已经摊销掉了。

Q5. 关于 Amdahl 定律在图优化中的正确理解,下列说法正确的是?

多选题 · hard

  • A.端到端加速受不可优化部分限制← 正确答案
  • B.单算子加速多少整体就能加速多少
  • C.应优先优化时间占比最大的部分← 正确答案
  • D.所有算子都能被无限加速到零时间

答案:AC。Amdahl 定律的核心是“短板决定天花板”:整体加速比上限是 1/(1−f),所以应优先优化占比最大的部分。B 是经典误解,忽略了不可优化部分;D 中的 IO、调度等开销无法归零,且并行加速存在硬件极限。

Q6. torch.compile 的 reduce-overhead 模式最核心的作用是什么?

单选题 · mid

  • A.消除 Python 调度与 kernel 启动开销← 正确答案
  • B.对算子做全自动逐层搜索调优
  • C.只验证模型正确性而不做优化
  • D.把模型参数做低比特量化压缩

答案:A。reduce-overhead 模式通过 CUDA Graph 把一长串 kernel 启动指令“录制成视频一次性播放”,大幅减少 Python 解释和 kernel 启动的固定开销。B 是 max-autotune 的职责;C 是 debug 模式;D 是量化压缩,不属于 torch.compile 的核心职责。

Q7. 评估一次图优化是否成功时,应该关注哪些指标?

多选题 · mid

  • A.端到端推理延迟下降幅度← 正确答案
  • B.单个 Pass 名称是否好听
  • C.峰值内存在线服务的稳定值← 正确答案
  • D.一次部署的编译总耗时← 正确答案

答案:ACD。效果分析的三把尺子是加速比(对应端到端延迟)、峰值内存、编译开销。端到端延迟最贴近用户体感,峰值内存决定能否部署在受限设备上,编译耗时决定回本快慢。Pass 名称只是手段的名字,不是效果指标。

Q8. 关于 Pareto 前沿上的优化配置,下列说法正确的是?

单选题 · hard

  • A.加速比在所有配置中最大
  • B.不存在不增加成本就更高收益的点← 正确答案
  • C.编译开销在所有配置中最小
  • D.一定使用最大数量的优化 Pass

答案:B。Pareto 前沿上的点意味着无法再“白嫖”:想提高收益必须增加成本,想降低成本必须牺牲收益。A 只追求收益,C 只追求成本,D 是“全堆 Pass”的误区。实际选择时还要结合 α 敏感系数在沿线上挑最符合业务需求的那个点。

Q9. 常量折叠优化主要作用于哪类计算?

单选题 · easy

  • A.运行期才能确定的动态数据
  • B.编译期值已知的常量表达式← 正确答案
  • C.需要大量内存的中间张量
  • D.没有输入依赖的深层神经网络

答案:B。常量折叠像“预制家具”:凡是编译期就能算出结果的表达式(如 2*x + 1/0.5),编译器直接算出最终值,运行时连算都不算。A 是动态数据的场景,属于运行时优化范畴;C 是内存问题;D 的规模描述与常量折叠无关。

Q10. 在自动调优选择图优化配置时,下列哪项做法是不正确的?

单选题 · extreme

  • A.考察线上真实流量下的延迟分布
  • B.考虑编译时间预算与缓存复用情况
  • C.关注算子覆盖率和硬件适配程度
  • D.只参考训练阶段吞吐量指标来做决策← 正确答案

答案:D。训练吞吐量与推理部署优化是两套目标:训练关心吞吐,推理关心延迟、峰值内存和编译开销。只参考训练指标而忽略线上延迟分布、编译预算、硬件适配,是典型的“拿错尺子量房”。A、B、C 都是部署场景下必须纳入评估的要素。