如何实现跨不同perf事件的差值分析?获取L2非L3停顿数据
解决方法
因为perf原生工具没有直接计算不同事件差值的功能,你可以通过导出事件数据到文本,再用脚本统计计算差值的方式实现需求,以下是三种可行的方案:
方案一:将两个事件录到同一份文件后导出计算
这种方式能避免两次录制带来的程序行为差异,结果更准确:
重新录制同时包含两个事件的性能数据:
perf record -e cycle_activity.stalls_l2_miss,cycle_activity.stalls_l3_miss -g ./你的程序(
-g用于记录调用栈,若不需要可以省略)将事件数据导出为结构化文本:
perf report -n --stdio -F comm,symbol,period,overhead | grep -E "(stalls_l2_miss|stalls_l3_miss|^ Symbol)" > perf_events.txt-n显示原始事件计数(period),--stdio强制文本输出,-F指定要导出的字段- 过滤命令只保留目标事件和符号表头,方便后续处理
用awk脚本计算差值(示例):
BEGIN { print "符号\tL2停顿计数\tL3停顿计数\t仅L2停顿计数\t仅L2占比(%)" print "--------------------------------------------------------" total_l2 = 0 } # 统计总L2停顿数(用于计算精确占比) /stalls_l2_miss/ { total_l2 += $NF } # 遍历符号和对应事件 /^ Symbol/ { sym = $2 } /stalls_l2_miss/ { l2_cnt = $NF l2_oh = $(NF-1) } /stalls_l3_miss/ { l3_cnt = $NF l3_oh = $(NF-1) diff_cnt = l2_cnt - l3_cnt diff_oh = (diff_cnt / total_l2) * 100 printf "%s\t%d\t%d\t%d\t%.2f\n", sym, l2_cnt, l3_cnt, diff_cnt, diff_oh }保存为
calc_diff.awk后运行:awk -f calc_diff.awk perf_events.txt
方案二:分别导出两份独立perf文件的数据再关联
如果已经有两份独立的perf文件,可以用这种方式:
分别导出两份文件的符号、计数和占比:
perf report -n --stdio -F symbol,period,overhead --input perf_l2.data > perf_l2.txt perf report -n --stdio -F symbol,period,overhead --input perf_l3.data > perf_l3.txt用
join和awk关联符号并计算差值:join -1 1 -2 1 <(sort perf_l2.txt) <(sort perf_l3.txt) | awk '{ diff_cnt = $2 - $4 diff_oh = $3 - $5 printf "%s\t%d\t%d\t%d\t%.2f%%\n", $1, $2, $4, diff_cnt, diff_oh }' > l2_only_stalls.txtsort确保符号能被join正确匹配,注意提前确保两份文件的符号解析环境一致(比如同一版本的调试符号)
方案三:用perf script导出原始采样数据统计
如果需要更细粒度的调用栈级分析,可以用这种方式:
录制包含两个事件和调用栈的数据:
perf record -e cycle_activity.stalls_l2_miss,cycle_activity.stalls_l3_miss -g ./你的程序导出原始采样数据:
perf script > perf_script_raw.txt用Python脚本统计调用栈级别的差值(示例思路):
from collections import defaultdict stack_counts = defaultdict(lambda: {'l2': 0, 'l3': 0}) current_stack = [] with open('perf_script_raw.txt', 'r') as f: for line in f: line = line.strip() if not line: # 空行表示采样结束,将当前栈转为字符串作为key if current_stack: stack_key = ';'.join(reversed(current_stack)) current_stack = [] continue # 判断事件类型 if 'cycle_activity.stalls_l2_miss' in line: event_type = 'l2' elif 'cycle_activity.stalls_l3_miss' in line: event_type = 'l3' else: # 提取栈帧符号(适配perf script的输出格式) parts = line.split() if len(parts) >= 2: current_stack.append(parts[-2]) continue # 统计对应栈的事件数 if stack_key: stack_counts[stack_key][event_type] += 1 # 输出结果 print("调用栈\tL2停顿\tL3停顿\t仅L2停顿") print("----------------------------------------") for stack, cnt in stack_counts.items(): diff = cnt['l2'] - cnt['l3'] if diff > 0: print(f"{stack}\t{cnt['l2']}\t{cnt['l3']}\t{diff}")
注意事项
- 若分开录制两份perf文件,必须保证程序运行的输入、环境完全一致,否则计数差异可能来自程序行为变化,而非事件本身。
- 调用栈分析时,需要确保程序有完整的调试符号(编译时加
-g参数),否则符号会显示为地址。
内容的提问来源于stack exchange,提问作者ajp
相关产品推荐
相关产品推荐

