想象一个深夜的案发现场:程序跑得奇慢无比,就像一场混乱的盗窃案,线索散落四处,真正的“主犯”藏在暗处。性能分析(Profiling)就是侦探的破案流程——破案的第一步永远是封锁现场、保护证据,而不是凭直觉乱抓人。同理,优化代码的第一步永远是“测量”,而不是瞎猜。 在程序世界里,每个函数都是一位“嫌疑人”,CPU 就是唯一的案发现场。谁在 CPU 上待得最久,谁就是拖慢程序的“主犯”。性能分析要做的事,就是搞清楚:每个函数在这段时间里到底占用了多少 CPU 时间?采样式 profiling 像保安查岗,每隔几秒探头看一眼谁在工位上,几乎不打扰任何人;插桩式 profiling 像给每个路口装监控摄像头,事无巨细全部记录;火焰图则是把所有线索拼成一张“案发全景图”,哪块“砖”最宽最烫,一眼便知。读完这篇文章,你将学会用 perf 这个“侦探工具箱”收集证据,用火焰图还原真相,最终精准定位并制服那个偷走性能的主犯。
性能分析是 AI 从业者的基本功——不懂 profiling,你永远在“盲调”代码;掌握 perf 与火焰图,你就能用数据说话,让每一次优化都有据可依。
可视化演示
性能分析Profiling通过采样和插桩识别程序热点,火焰图直观展示调用栈时间分布。本文讲解采样profili... 切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
采样与插桩P:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现
性能分析概述
侦探破案的第一步永远是封锁现场、保护证据,而不是先猜凶手。性能分析也是一样——先测量、后优化。很多工程师拿到一个慢程序,第一反应是“我觉得是这里慢”,然后埋头改三天。这就像把全城警力都派去蹲守一个根本没参与作案的小角色,真正的主犯早溜了。
所谓性能分析(Profiling),是指在程序运行过程中收集其行为数据,从而回答一个核心问题:时间都花在哪了?通过测量,我们能得到一张“成本分布表”——每个函数占用的 CPU 时间比例。这张表是后续所有优化决策的依据,没有它,一切优化都是盲人摸象。
为什么人的直觉靠不住?因为现代程序的性能特征往往反直觉:一个被调用百万次的小函数,每次只花 1 微秒,加起来就是 1 秒;而一个看起来很复杂的大函数,可能只被调用一次。前者在火焰图上会是又宽又高的“平顶山”,后者却是微不足道的一小块。量化每个函数的耗时占比,是 profiling 最根本的目标,也是性能优化这条破案之路的起点。
采样式Profiling
想不打扰任何人,又想掌握全场的动态?答案就是“查岗”。保安不会一直跟在每个人身后,而是每隔几分钟到各办公室门口探头看一眼,记录“这一刻谁在工位上”。只要查岗次数足够多,统计出的“在场时长比例”就会非常接近真实。采样式 profiling(sampling profiler)就是程序世界的查岗。
它的实现原理很优雅:利用操作系统定时器或硬件中断,按固定频率(比如每秒 99 次)短暂地中断程序,记录下当前 CPU 的程序计数器(PC)和完整调用栈,然后继续运行。整个过程对程序几乎是透明的。分析结束后,统计每个函数被“抓拍”到的次数占比,即可近似估计其 CPU 占用比例。
这个估计的数学基础是概率统计。假设一次分析共采集了 \(N_{\text{total}}\) 个样本,其中函数 \(f\) 被撞见 \(N_f\) 次,那么:
就像保安查岗从不打扰员工工作,采样 profiling 最大的优点是开销极低、几乎不改变程序行为,非常适合在生产环境或大型程序中使用。但它只能给出统计结论,无法精确回答“这个函数到底被调用了多少次”——想要精确答案,就得请出下一位选手:插桩。
- \(N_f\)函数被“抓拍”到的次数:在每一次“查岗快照”中,如果当前正在执行的函数是 \(f\),就给它的计数器加一。
- \(N_{\text{total}}\)总查岗次数:采样频率乘以分析时长,就是整场调查拍下的快照总数。
- \(P(f)\)CPU 占用率的估计值:从“被撞见”的比例推算真实占用比例,样本数越大越接近真实值。
插桩式Profiling
如果采样是“远远地看一眼”,那插桩式 profiling(instrumentation)就是给程序的每个关键路口都装上一台监控摄像头,逐帧录像。它会在函数入口、出口甚至循环体内部嵌入一段记录代码(称为探针 probe),每次执行都自动“打卡”。于是我们得到的不再是概率估计,而是精确的流水账。
插桩有两条实现路径:一是编译器插桩,在编译期自动注入探针;二是二进制改写,直接修改可执行文件。无论哪条路,探针都会做三件事:记录当前时间戳、累计调用次数、累计耗时。依赖这些数据,你能精确知道“这个函数被调用了 100 万次,平均每次 2 微秒”。
但天下没有免费的午餐。每次执行探针都要额外的 CPU 周期——读取时钟、更新计数器、写入内存,这都会真实地拖慢程序。尤其当函数被高频调用时,插桩开销会被放大,甚至改变线程的时序关系。这就是性能分析中著名的“观察者效应”:拿着温度计去量水的温度,温度计本身也会改变水温。
监控装的越密,记录越精确,但干扰也越大。所以在实战中,我们要像侦探权衡“隐蔽蹲守”与“清楚录像”一样做取舍。幸运的是,现代 Linux 上有一个工具能让我们两全其美——它直接利用 CPU 自带的“黑匣子”,几乎不产生任何额外开销。这就是 perf。
- \(r\)插桩的开销比例:每次“打卡”动作消耗的时间占函数真实耗时的比重,取决于探针复杂度和调用频率。
- \(T_{\text{real}}\)真实耗时:没有任何监控时程序本来的执行时间,是我们真正想测却因为“测量本身”而测不到的基准值。
- \(T_{\text{measured}}\)实测耗时:插桩后观察到的数字,它总是比真实值大一点——就像装了监控后,保安巡逻本身也增加了现场的人流量。
perf性能工具
perf 是侦探的百宝工具箱,里面既有望远镜(采样),也有微型录音笔(硬件计数器),更有全套的取证流程。它之所以强大,是因为它绕开了“插桩改代码”的老路,直接访问 CPU 内部的性能监控单元(PMU,Performance Monitoring Unit)。
现代 CPU 芯片里内置了大量硬件性能计数器,可以近乎零开销地记录底层硬件事件:CPU 周期数、执行的指令条数、缓存未命中次数、分支预测失败次数等等。这些计数器由硬件直接维护,不需要修改程序,就像飞机上的黑匣子——无论飞行员有没有主动记录,它都在自动工作。
perf 的另一半能力来自内核软件事件:上下文切换、页错误、CPU 迁移等。二者合起来,perf 就能回答两类问题:“CPU 在忙什么”(采样调用栈)和“CPU 为什么低效”(缓存 miss、分支预测失败)。最常用的命令是:
意思是:以每秒 99 次的频率采样,记录调用栈,运行程序。结束后执行 perf report 就能在交互界面里查看热点函数。这里有个小讲究:99Hz 而不是 100Hz,是为了避开与系统 100Hz 的调度心跳产生共振,防止采样偏差。
有了 perf 这个取证利器,侦探就收集到了海量的原始证据——但这些证据是碎片化的,几千条调用栈堆在眼前反而看不清。这时候,需要一张能把所有线索拼起来的全景图。于是,火焰图登场了。
perf record -F 99 -g -- ./my_program火焰图技术
火焰图就像是侦探办公室里挂起的那面“证据墙”:把每位嫌疑人的在场时间画成一块块砖,砖越宽,说明这个人在现场待的时间占比越高。它由 Linux 性能专家 Brendan Gregg 发明,如今已是性能分析的标配可视化工具。
火焰图本质上是一个自底向上的调用栈树。Y 轴是调用深度,最底层是 main 函数,越往上越是叶子函数;X 轴不是真实时间线,而是“采样占比”——一块砖的宽度等于该函数在所有采样样本中出现的比例。同一层内,砖块按宽度从大到小排列,就像一簇向上的火苗。
生成火焰图的标准流程是四步曲:perf record 采集调用栈 → perf script 导出原始数据 → stackcollapse-perf.pl 把海量原始栈折叠成“栈 + 计数”的紧凑文本 → flamegraph.pl 渲染成可交互的 SVG 图片。每一个砖块的宽度,都有严谨的数学含义:
学会读火焰图,就等于学会了看“证据墙”:宽而平的顶部(人送外号“平顶山”)意味着一个被高频调用的短函数在疯狂消耗 CPU;高耸的“烟囱”则提示存在很深的递归调用链。但记住,火焰图只告诉你“谁占了多少时间”,至于这个时间是花在 CPU 计算上还是等内存上,还需要把 CPU profiling 与内存 profiling 结合来看。
- \(T_f\)函数 f 的累计被采样时间:把调用栈每一帧中出现 f 的样本数乘以单位采样间隔,就得到 f 在“证据墙”上的占地总时长。
- \(T_{\text{total}}\)整场分析的总时长:所有采样样本数乘以单位采样间隔,即整个 profiling 窗口的耗时。
- \(W_f\)砖块宽度:它直观反映 f 的 CPU 时间占比。\(W_f\) 越大,f 越可能是优化的主攻方向。
CPU与内存Profiling
侦探既要关心嫌疑人在现场待了多久(CPU 时间),也要关心他有没有频繁地“搬进搬出大量行李”(内存占用)。CPU profiling 与内存 profiling,是性能分析的两条主线——一条查“时间去哪了”,一条查“内存去哪了”。
CPU profiling 的核心是采样:通过 perf 等工具周期性记录调用栈,统计各函数的 CPU 周期消耗,定位计算热点。它回答的是“谁在烧 CPU”——比如某个排序函数占用了 40% 的 CPU 时间,那它就是不折不扣的计算热点。
内存 profiling 则完全不同,它追踪内存的分配、释放与驻留情况。常用工具包括 heaptrack、Valgrind Massif,以及编程语言自带的内存采样器。它回答的是“谁占着内存不还”——比如某个缓存模块在无限增长,或者某个循环在反复创建大对象导致垃圾回收压力飙升。内存 profiling 最关心的三个指标,都围绕着一个核心量:驻留内存随时间的变化曲线。
CPU 和内存两条线索交叉对比,往往能发现更深的系统性问题——比如对象频繁分配导致 GC 压力,而 GC 线程反过来又烧掉了大量 CPU。当证据链完整时,侦探终于可以锁定主犯了。接下来要做的,就是制定抓捕方案:热点分析与优化。
- \(M(t)\)t 时刻的驻留内存量:在时间轴上逐点测量内存占用,每一瞬间都有一个“行李重量”。
- \(\max\)取极大值:在整段运行时间里扫描内存曲线,找到最“臃肿”的那个瞬间。
- \(M_{\text{peak}}\)峰值内存:它决定系统要为你的程序预留多少内存,过高时极易触发 OOM 崩溃。
热点分析与优化
证据收集完毕,主犯浮出水面——那些在火焰图上又宽又亮的函数。但侦探不会一上来就掏枪,而是先计算一下“抓捕收益”:把这个人拿下,案子能推进多少?对应到性能优化,就是评估每个热点函数的优化回报率。
优化回报率遵循二八法则(帕累托定律):通常 80% 的 CPU 时间集中在 20% 的代码上。与其平均用力优化所有函数,不如集中火力攻击占比最高的那一个。假设我们从 profiling 中发现 process_data 占 58% 的 CPU 时间,而 parse_input 只占 26%,那么即便后者优化得再极致,收益也远不如前者的大幅改进。
具体优化方向有三个层次:算法层(换更优的算法或数据结构,如把 O(n²) 改成 O(n log n))、编译器层(开启 -O2/-O3、函数内联、循环向量化)、架构层(引入缓存、异步处理、并行计算)。但无论选哪个方向,都必须遵循同一个闭环工作流:测量 → 假设 → 修改 → 复测。
至此,侦探的完整破案流程走完了:查岗(采样 profiling)、装监控(插桩 profiling)、取证(perf)、拼图(火焰图)、抓捕(热点优化)。但请注意,程序的世界里没有“结案”——业务在变、数据在变、环境在变,今天的优化热点明天可能就换了位置。所以 profiling 从来不是一次性的动作,而是伴随软件生命周期的常态化破案手段。每一次性能退化,都请从 profiling 开始。
- \(T_{\text{before}}\)优化前的热点耗时:基线数据,来自第一轮 profiling 得到的函数占比乘以总运行时间。
- \(T_{\text{after}}\)优化后的耗时:修改代码后,用同样的 profiling 流程再次测量得到的新数值。
- \(\Delta T\)真正节省的时间:只有 \(\Delta T\) 为正且足够大,优化才算有效;如果变化不大,说明你优化错了地方。
AI 应用场景
线上推理服务 CPU 飙高排查
阶段:模型服务性能优化
解决的问题:AI 推理服务上线后 CPU 使用率长时间 100%,P99 延迟翻倍。传统排查方式靠逐段添加日志猜测,既慢又干扰线上运行;改用 perf 采样收集调用栈并生成火焰图后,一眼看到热点集中在数据预处理的重采样和归一化函数上,而非模型推理本身。
具体案例:某推荐系统团队发现特征拼接阶段在火焰图上占比 65%,深入分析发现是逐字段字符串拼接导致的隐式内存拷贝。改用预分配缓冲后,P99 延迟从 120ms 降到 80ms,CPU 使用率下降 40%。
大数据批处理作业调优
阶段:数据管道性能分析
解决的问题:特征工程批处理作业每晚超时,直接影响次日模型更新。用采样 profiling 定位到 Shuffle 阶段某自写 UDF 占用 60% CPU,而该 UDF 的功能其实可以用内建函数替代。
具体案例:某广告 CTR 特征工程作业,通过 perf 发现用户 ID 哈希函数是热点,替换为更快的非加密哈希后,整个作业时长缩短 35%,数据产出时间提前到凌晨三点前。
训练框架内存泄漏排查
阶段:训练稳定性保障
解决的问题:模型训练在增大 batch size 后内存持续增长,最终 OOM 崩溃。用内存 profiling 追踪分配路径,发现是 dataset iterator 中一个全局列表不断追加历史 loss 且从未清理,导致驻留内存随时间线性增长。
具体案例:PyTorch 训练循环中,每次迭代都向全局列表追加 loss,跑 2 万步后内存从 4GB 涨到 32GB。内存火焰图直接暴露了这一分配热点,修复后训练稳定运行一周无 OOM。
Python 演示
import randomimport math # 定义程序的"真实行为":每个函数实际占用 CPU 的比例REAL_RATIO = { "process_data": 0.55, "parse_input": 0.25, "format_output": 0.12, "misc_work": 0.08,} def run_program(total_time=20000): """模拟执行程序,返回每个时间点正在运行的函数名列表""" timeline = [] for _ in range(total_time): r = random.random() acc = 0.0 chosen = "misc_work" for name, ratio in REAL_RATIO.items(): acc += ratio if r < acc: chosen = name break timeline.append(chosen) return timeline def sampling_profile(timeline, interval=7): """模拟采样式 profiling:每隔固定间隔查看一次当前运行函数""" samples = {} for i in range(0, len(timeline), interval): func = timeline[i] samples[func] = samples.get(func, 0) + 1 total = sum(samples.values()) return {name: cnt / total for name, cnt in samples.items()}, total def instrumentation_profile(timeline): """模拟插桩式 profiling:记录每一次函数切换,精确统计""" counts = {} for func in timeline: counts[func] = counts.get(func, 0) + 1 total = len(timeline) return {name: cnt / total for name, cnt in counts.items()}, total def error_rate(est): """计算估计值与真实值的平均绝对误差""" total_err = 0.0 for name in REAL_RATIO: total_err += abs(est.get(name, 0) - REAL_RATIO[name]) return total_err / len(REAL_RATIO) if __name__ == "__main__": time_series = run_program(20000) sample_est, sample_n = sampling_profile(time_series) exact_est, exact_n = instrumentation_profile(time_series) print("真实占比: ", REAL_RATIO) print("采样估算(样本数=%d):" % sample_n, sample_est) print("插桩精确统计: ", exact_est) print("平均误差: 采样 %.4f | 插桩 %.4f" % ( error_rate(sample_est), error_rate(exact_est))) for name in REAL_RATIO: err = abs(sample_est.get(name, 0) - REAL_RATIO[name]) print("采样误差(%s): %.2f%%" % (name, err * 100))
def run_program(total_time=20000):模拟程序执行,按真实占比随机决定每个时间点运行的函数,生成一条“运行时间线”作为实验基准。def sampling_profile(timeline, interval=7):核心模拟:每隔 7 个时间单位“偷看”一次当前函数,累计计数后计算占比,对应正文中的查岗式采样。def instrumentation_profile(timeline):遍历整条时间线逐时刻计数,得到完全精确的占比,对应插桩式 profiling 的“全程录像”。total = sum(samples.values())样本总数 \(N_{\text{total}}\),它是占比估计的分母,也是采样开销的体现。error_rate(est)计算估计值与真实占比的平均绝对误差,直观展示“样本越多,误差越小”的统计规律。
常见误区
误区火焰图上哪个函数砖块最宽,哪个就是性能瓶颈,直接拿它开刀准没错。
正解砖宽代表的是“采样占比”,即 CPU 时间占比,但高占比不等于可优化空间大。比如一段纯数学计算本就该占 90% 的 CPU 时间,这是合理开销;而一段本不该出现的意外序列化即使只占 5% 也值得优先消除。要像侦探判断“在场时间长”是否等于“作案”,还需结合调用关系与业务逻辑。正确的做法是看“可避免成本”,而非只看宽度。
为何常见:火焰图的视觉冲击力太强,“最烫”的直觉让人下意识认为宽砖就是优化的第一目标,忽略了占比与优化价值是两回事。
误区把采样频率从 99Hz 提到 999Hz,甚至 9999Hz,得到的数据肯定越来越接近真相,反正只是多记几笔。
正解采样误差确实随样本数增加而减小,但收益是递减的——误差大致按 \(1/\sqrt{n}\) 缩小,样本数翻 4 倍误差才减半。更关键的是,过高的采样频率会让“查岗”本身变成沉重的负担,极大地干扰程序运行,反而引入了插桩才有的观察者效应。99Hz 是实践检验出的甜点值:既能保证统计精度,又不至于影响程序行为。
为何常见:人们凭“样本越多越准”的朴素统计直觉做决策,却忽略了采样本身的成本以及误差递减规律。
误区插桩式 profiling 能精确记录每次调用和耗时,数据是“确定的”,而采样只是“估计的”,所以插桩永远更可信。
正解插桩会显著改变程序的运行行为。探针的时间开销、缓存污染、线程调度时序偏移,都会让测量结果失真。一个真实耗时 1ms 的高频函数,插桩后可能被测成 3ms。而采样式 profiling 在绝大多数生产场景下“隐身”效果更好,测出的数据反而更接近真实行为。这也是火焰图的标准流程使用 perf 采样而非插桩的原因。
为何常见:人类天然偏爱精确数字,容易忽略“测量行为本身会改变被测对象”这一微妙事实,就像拿着热水温度计去量冷水,温度计本身也在改变水温。
误区perf 就是看 CPU 占用率的工具,要分析内存或 IO 得换别的工具,perf 帮不上忙。
正解perf 是一个通用的性能事件采样框架。除了 CPU 周期,它还支持缓存未命中、分支预测失败、页错误、上下文切换、CPU 迁移等几十种硬件与软件事件。你可以用 perf record -e cache-misses 定位缓存失效热点,用 perf record -e page-faults 追踪内存页错误。它不是单一工具,而是一个覆盖 CPU、内存、锁竞争的全景探测器。
为何常见:perf 最常见的演示场景就是 CPU 采样与火焰图,用途单一的印象根深蒂固,导致很多人低估了它的能力边界。
误区我对自己的代码很熟,哪里慢心里大概有数,直接改就行了,跑 profiling 是浪费时间。
正解大量研究表明,工程师对性能瓶颈的直觉预测准确率极低。很多“看起来复杂”的函数实际占比不到 1%,而真正的热点往往藏在日志格式化、隐式内存拷贝、字符串拼接这些不起眼的地方。按“测量 → 假设 → 修改 → 复测”的闭环推进,才是工程正规军的做法。省掉 profiling 相当于侦探跳过取证直接抓人,抓错人的概率极高。
为何常见:过度自信加上省事心理,让人倾向于用“聪明的猜测”替代“笨拙的测量”,结果反而浪费更多时间在无效优化上。
面试题
Q1. 采样式 profiling(sampling profiler)的基本原理是什么?
- A.在每行代码插入计时器
- B.以固定频率中断程序并记录当前调用栈← 正确答案
- C.修改操作系统调度器
- D.静态分析源代码复杂度
答案:B。采样式 profiling 像保安查岗——每隔固定时间把程序短暂中断,看一眼当前正在执行哪个函数并记录调用栈,多次采样后统计占比。A 描述的是插桩式做法;C 和 D 与采样机制无关。
Q2. 火焰图的 X 轴代表什么?
- A.真实时间轴
- B.函数被采样到的次数占比← 正确答案
- C.内存地址
- D.CPU 频率
答案:B。火焰图的 X 轴不是时间线,而是每个函数在全部样本中被采样到的比例,所有砖块宽度加起来约等于 100% 的 CPU 时间。正因为 X 轴是占比而非真实时间,火焰图才能直观展示“时间都花在哪个函数上”。
Q3. 在火焰图中,顶部出现宽而平的“平顶山”通常表示什么?
- A.存在很深的递归调用链
- B.一个被大量调用且占用 CPU 较多的短函数← 正确答案
- C.程序的入口函数
- D.系统发生了死锁
答案:B。宽而平的顶部意味着栈顶叶子函数被高频反复触发,每次执行时间很短,但总占比很高——就像保安查岗总在同一个办公室门口看到同一个人频繁进出。这类“平顶山”是优化的重点信号;高耸的烟囱才对应深层递归。
Q4. 插桩式 profiling 相比采样式 profiling 的显著缺点是?
- A.无法获得调用栈
- B.测量结果总是偏低
- C.额外运行时开销可能改变程序行为← 正确答案
- D.只能用于 Java 程序
答案:C。插桩在每个函数出入口加探针,像给每个员工装打卡机。打卡行为本身消耗时间和缓存,对高频调用函数影响明显,可能把 1ms 的函数测成 3ms,这就是“观察者效应”。A 错误,插桩恰恰能精确记录调用关系;B 应为“偏高”。
Q5. 命令 perf record -F 99 -g 中 -F 99 的含义是什么?
- A.记录 99 秒数据
- B.以每秒 99 次的频率采样← 正确答案
- C.记录 99 个函数
- D.每秒打印 99 行日志
答案:B。-F 99 表示以 99Hz 频率采样,即大约每 10ms 抓拍一张“查岗快照”。99Hz 是 Brendan Gregg 推荐的采样率,能避开与系统 100Hz 调度心跳的共振,从而减少采样偏差。
Q6. 火焰图生成的标准流程中,用于将 perf 原始数据转换为“栈 + 计数”折叠文本的工具是?
- A.stackcollapse-perf.pl← 正确答案
- B.flamegraph.pl
- C.perf top
- D.gprof
答案:A。标准流程是 perf record → perf script 导出 → stackcollapse-perf.pl 折叠成紧凑文本 → flamegraph.pl 渲染 SVG。flamegraph.pl 是最后画图的步骤;perf top 是实时监控工具;gprof 是另一套独立的插桩分析工具。
Q7. 关于采样 profiling 的统计误差,下列说法正确的是?
- A.误差与样本数成正比
- B.增大样本数 n 时,误差大致按 1/√n 减小← 正确答案
- C.采样频率超过 1000Hz 后误差为零
- D.插桩的误差一定比采样小
答案:B。采样本质是随机抽样,估计误差与 √n 成反比——样本翻 4 倍,误差减半。A 说反了;C 中更高频率会引入巨大开销,且量化误差不可能为零;D 忽略了插桩的观察者效应,其误差未必更小。
Q8. 下列哪些属于 perf 支持的硬件性能计数器事件?
- A.CPU 周期数← 正确答案
- B.缓存未命中(cache-misses)← 正确答案
- C.分支预测失败← 正确答案
- D.Python 异常次数
答案:ABC。perf 利用 CPU 内置的 PMU 计数器,可统计 CPU cycles、cache-misses、branch-misses 等底层硬件事件。Python 异常属于软件层事件,不归硬件性能计数器管,无法用 perf 直接统计。
Q9. 内存 profiling 的核心关注指标包括哪些?
- A.峰值内存 M_peak← 正确答案
- B.内存分配次数与对象大小← 正确答案
- C.驻留内存随时间增长趋势← 正确答案
- D.磁盘剩余空间
答案:ABC。内存 profiling 关心“内存去哪了”——峰值决定是否 OOM,分配频率揭示频繁创建对象的压力,增长趋势暴露泄漏嫌疑。磁盘剩余空间属于存储监控,与进程的内存占用无关。
Q10. 在性能优化工作流中,正确的做法包括哪些?
- A.先 profiling 定位热点,再动手优化← 正确答案
- B.优化后重新 profiling 验证收益← 正确答案
- C.优先优化 CPU 占比最高且可避免的开销← 正确答案
- D.直接凭代码直觉修改最复杂的函数
答案:ABC。正规军步骤是“测量 → 假设 → 修改 → 复测”,用数据说话。A 是出发点,B 形成闭环验证,C 是二八法则的落地。D 的“凭直觉”正是 profiling 要纠正的误区——最复杂的函数往往不是真正的瓶颈。