perf is the user-space tool for Linux perf_events. It can both count total events and sample call stacks when events occur. First, clarify the question you want to answer: How much CPU did the program consume? Where are the hotspots? Why is a specific instruction slow? Or what is the request waiting for? Different questions require different events and observation scopes.
The following commands use the Linux user-space program ./prog as an example. Event support depends on the CPU, kernel, virtualization environment, and perf version; use perf list to check what is actually available. Permissions also depend on perf_event_paranoid, capabilities, and system policies; not all operations require root.
Counting: Establish a Repeatable Baseline
perf stat -r 5 -e task-clock,context-switches,page-faults -- ./progperf stat -r 5 -e'{cycles:u,instructions:u}' -- ./progperf list
The first command starts with CPU time, scheduling, and page faults; the second groups user-space cycles and instructions into the same group for easier comparison within the same measurement window. -r 5 runs the program five times; input, data state, and warm-up strategies must be consistent. If the program modifies data, prepare a repeatable test scenario first.
Metric
Question Answered
Cannot Directly Infer
task-clock
How much CPU time the task accumulated
Not equal to wall-clock time; multi-threaded accumulation can be larger
instructions / cycles
IPC for this load
Cannot use a uniform threshold to judge good/bad across CPUs and loads
branch-misses / branches
Miss ratio of counted branches
Looking at miss rate alone cannot determine total time contribution
page-faults
Number of page fault events
Not all are disk I/O; includes minor faults
context-switches
Frequency of scheduling switches
Many switches do not necessarily mean the primary bottleneck
For example, after rewriting an algorithm, instruction count drops by half, and IPC also drops, but total execution time might still be shorter. IPC should be interpreted in conjunction with execution time and completed work. The u in cycles:u means user-space only, not eliminating frequency changes; CPU frequency, SMT, migration, hybrid cores, and background interference still affect comparisons.
The specific mapping of generic cache-misses is determined by the PMU and should not be assumed to be the L1 miss rate. When discussing a specific cache level, select the event defined in that CPU's documentation and verify the denominator.
Same Work, Lower IPC Can Still Be Faster
The data below is hypothetical, intended for practicing counting interpretation, not measurement results from a specific machine. Both A and B complete 10,000 identical units of work, with consistent event scope and running conditions.
Metric
A
B
Explanation
Wall-clock time
0.50 s
0.40 s
B takes 20% less time
instructions
2.0 × 10⁹
1.2 × 10⁹
Instructions per unit drop from 200,000 to 120,000
cycles
1.0 × 10⁹
0.8 × 10⁹
User-space event counts decrease
IPC
2.0
1.5
B has lower IPC but does less total work
The correct conclusion here is that B completes the same work faster; do not judge it as a regression just because IPC dropped from 2 to 1.5. If B only processed 8,000 units, skipped validation, or swallowed errors, the reduced time cannot be directly counted as optimization benefit.
A 20% reduction in time corresponds to a 25% increase in throughput, because their denominators differ. task-clock also requires the correct denominator: if 3.2 seconds of cumulative CPU time are recorded within 2 seconds of wall-clock time, the average is approximately 1.6 CPUs of execution time, not "CPU utilization exceeded 100% so the data is broken." This average still does not represent peak usage at any moment or uniformity across cores.
Counter Multiplexing: Numbers May Be Scaled
Hardware counters available simultaneously are limited. When events exceed available resources, they may be time-multiplexed. perf can scale results based on actual running time, but if the load is highly phased, briefly observing one phase and extrapolating to the whole process introduces error.
Pay attention to the running ratio, not counted, or not supported in the output. Reducing the number of events, grouping related ratio events, and repeating experiments help judge result stability; grouping may also fail to schedule entirely due to resource constraints. See the manual for perf stat scaling and event group semantics.
Scaling Correction Covers Time, Cannot Recover Unobserved Phases
Assume an event is enabled for 10 seconds, but actually scheduled by hardware counters for 2.5 seconds, with an original count of 200. Scaling by time ratio yields 200 × 10 / 2.5 = 800. This implies the assumption that the sampled period represents the unsampled period; if it happens to cover only the initialization peak, while the later large amount of time is in another work phase, 800 may severely deviate from the true total.
Related ratios are best derived from the same event group and compatible scope, but event groups are not a free guarantee: if hardware resources are insufficient, the entire group may fail to run. When encountering not supported, first check the event and PMU; when encountering not counted, check scheduling resources and actual running time. Do not treat these statuses as numerical zero for division. perf stat scaling and grouping
Why can time scaling still be wrong?
The full bars are ground truth supplied by the model; real measurements lack unseen intervals. Observing the first two seconds scales 200 to 1000 instead of 280; a late low-rate window underestimates. With running=0, report no valid count, not zero events.
N=Nrawtrunningtenabled,trunning>0
Preparing the visual
Why can time scaling still be wrong?
enabled/running scaling adjusts coverage, not unseen phase rates. A window covering a peak or trough can bias the total estimate.
{"id":"perf-multiplex-estimate","title":"Why can time scaling still be wrong?","summary":"enabled/running scaling adjusts coverage, not unseen phase rates. A window covering a peak or trough can bias the total estimate.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"start\" data-i18n=\"start\"></label><output for=\"start\" id=\"start-value\"></output><input id=\"start\" type=\"range\" min=\"0\" max=\"9\" value=\"0\" step=\"1\"></div><div class=\"control\"><label for=\"length\" data-i18n=\"length\"></label><output for=\"length\" id=\"length-value\"></output><input id=\"length\" type=\"range\" min=\"0\" max=\"10\" value=\"2\" step=\"1\"></div><div id=\"chart\" class=\"hist\" role=\"img\" data-i18n-label=\"chart\"></div><div class=\"metrics\"><div><span data-i18n=\"raw\"></span><strong id=\"raw\"></strong></div><div><span data-i18n=\"estimate\"></span><strong id=\"estimate\"></strong></div><div><span data-i18n=\"truth\"></span><strong id=\"truth\"></strong></div><div><span data-i18n=\"coverage\"></span><strong id=\"coverage\"></strong></div></div><p class=\"formula\" id=\"equation\"></p><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.hist{height:165px;display:grid;grid-template-columns:repeat(10,minmax(0,1fr));gap:4px;align-items:end;padding-bottom:25px;margin-top:15px}.hist div{position:relative;background:var(--surface);border:1px solid var(--rule);min-height:1px}.hist div.on{background:var(--accent-soft);border-color:var(--accent)}.hist small{position:absolute;top:100%;width:100%;text-align:center;font-size:10px}.metrics strong{font-size:18px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function draw(){const start=+$('#start').value,n=Math.min(+$('#length').value,10-start),rates=Array.from({length:10},(_,i)=>i<2?100:10),raw=rates.slice(start,start+n).reduce((a,b)=>a+b,0);set('start-value',start);$('#length').max=10-start;$('#length').value=n;set('length-value',n);$('#chart').innerHTML=rates.map((r,i)=>'<div class=\"'+(i>=start&&i<start+n?'on':'')+'\" style=\"height:'+r+'%\"><small>'+i+'</small></div>').join('');set('raw',raw);set('estimate',n?(raw*10/n).toFixed(1):'—');set('truth',280);set('coverage',10*n+'%');set('equation',n?'N̂ = '+raw+' × 10 / '+n+' = '+(raw*10/n).toFixed(1):'time_running = 0; N̂ = ∅');$('#estimate').dataset.value=n?raw*10/n:'';}document.querySelectorAll('input').forEach(e=>e.oninput=draw);draw();","audio":false,"strings":{"chart":"Events per second; selected bars were observed","coverage":"Time coverage","estimate":"Scaled estimate","heading":"Why can time scaling still be wrong?","intro":"Move the active counter window; compare scaling with the known model total.","length":"Counter running time (s)","raw":"Raw observed events","reset":"Reset","scope":"Ten one-second bins: 100 events/s for the first two, then 10/s for eight. True total 280. Synthetic rates, not PMU measurements.","scopeLabel":"Model scope and assumptions","start":"Window start (s)","truth":"True model total"}}
Sampling: Locate Execution Position When Events Occur
This set of commands selects the user-space cycle event, samples at a target frequency of 99 Hz, and collects user stack information. The actual sampling frequency may be throttled by the kernel or affected by the workload; do not rely on default events or frequencies of a specific version.
-F refers to the target sampling frequency, while -c refers to the event sampling period; their meanings are different. Cycles samples answer the distribution of events when the CPU is active; during blocking periods, threads are not running, and standard on-CPU stacks will not directly tell you the complete wait time. To interpret lock waits or I/O latency, combine with scheduling events or entry/return tracing.
There are trade-offs in call stack methods: the frame pointer method usually requires the relevant code to preserve frame pointers; the DWARF method records the user stack for unwinding, requiring corresponding unwind information, and the sampled stack size affects data volume and truncation; LBR depends on hardware support. -g is not a switch that guarantees a complete call chain for any binary. perf record
Symbol names, source code lines, and call chains are different pieces of information. Preserve the matched built binary, build-id, and debug symbols; avoid using files from another compilation to interpret an old perf.data. Inlining, tail calls, and compiler optimizations affect source correspondence; Linking and Loading explains the relationship between these artifacts.
Flame Graphs: Aggregating Call Stacks
In standard aggregated flame graphs, the left-right position has no temporal meaning; width represents the amount or weight of samples assigned to that call stack. Colors usually just distinguish boxes and do not inherently represent temperature or time consumption levels. Looking up along the wide frame finds deep hotspots, but hotspots may also be caused by upper layers calling repeatedly. Brendan Gregg: Flame Graphs
These scripts belong to the FlameGraph project and are not built-in subcommands of perf. When samples are lost, symbols are missing, or stack unwinding is broken, first fix the input quality; do not just look at whether the generated graph looks pretty. Distinguish between counting sample ratios and event-period-weighted proportions according to report settings.
self and children are two types of attribution; do not add them repeatedly
A sample stack is main → handle_request → parse. Attributed by self, it falls at the current execution position of parse; attributed by children or call chain accumulation, handle_request and main will also include this sample. Thus, the percentages of parent and child rows may add up to more than 100%, which does not mean perf calculated incorrectly.
You can view reports with and without children accumulation separately and record the percentage basis used. A very wide parent frame may execute almost no code itself, serving only as a common caller for all hotspots; in this case, changing one line at the parent function entry will not naturally eliminate child call costs. perf report attribution
When sampling is too low, first increase effective observation
At 99 Hz, a single-threaded CPU-intensive phase running for only about 20 ms has an ideal magnitude of only about two samples, and actual results are further affected by events and scheduling. At this point, "a function accounts for 50%" may correspond to only one sample, and it is not worth ranking precisely based on this.
You can have the same workload repeat enough times within one process, handling initialization and steady state separately; do not change the input scale to a completely different algorithm path just to increase samples. Increasing frequency also has costs, potentially expanding sample data, losing events, or changing the observed program. First verify the number of effective samples, loss prompts, and call stack quality before considering increasing the frequency.
Does a wide parent mean expensive self time?
By default handle occupies 90% but has zero self weight: all those samples end in parse or send. main’s 100%, handle’s 90% and parse’s 60% overlap, so adding them does not give total time. Width changes track weights, not execution order.
Preparing the visual
Does a wide parent mean expensive self time?
Widths represent sample weights; parents include child calls. One sample can contribute to several ancestors but only one self leaf. Horizontal position is not time.
Ordinary sampling may have skid between event overflow and recording the instruction position; the instruction marked as a hotspot may not be the instruction that triggered the event. Precise sampling capabilities depend on specific PMUs and event bindings; Intel PEBS and AMD IBS are different hardware mechanisms.
The p modifier in perf indicates the requirement for sampling IP precision; pp requests zero skid, and ppp's strict support is determined by the event and hardware. Do not write cycles:ppp as the "highest precision mode" applicable to all machines. First query supported precise events, then interpret results based on their event definitions. perf list event modifiers
Precise IP does not mean it has been proven that the instruction is the root cause: cache hierarchy, data sources, dependency chains, or upstream control flow still require further observation. Top-down metrics can classify execution slots and other resources by microarchitecture, but support scope and metric definitions should be confirmed according to the local PMU; do not fix them as "the same after a certain generation."
The tool will report the actual event name; then pass that name to perf record -e. Command parsing of symbols requires that the target indeed has probeable locations; source code line and local variable parsing may also require DWARF. Inlining, optimized-out variables, or one symbol corresponding to multiple locations cannot be simplified to "any function, any line can be probed."
Recording parameters must verify the target function ABI and probe location; do not directly apply x86-64 register examples to arm64. At the end, delete the probe created this time using the full actual event name, rather than clearing the shared tracing facility. perf probe manual
Attaching to a process uses -p PID, and threads use -t TID. System-wide short collections can use perf record -a ... -- sleep 10, but this observes other workloads; when selecting CPU, process, user-space, or kernel-space, ensure the conclusion matches the actual scope.
Constrain Expected Benefits with Request Time Accounting
Assume CPU samples are time-representative; parse accounts for 40% of 20 ms CPU work, which is 8 ms; accelerating it by two times only saves 4 ms. If the request also has 100 ms of serial wait, the total drops from 120 ms to 116 ms, not "hotspot accelerated by two times, interface also becomes twice as fast."
Multi-threaded pipelines, asynchronous overlap, and queuing systems cannot simply add the CPU time of each thread to the single-request latency. Under high load, reducing CPU service time may also reduce queuing; under low load, it may show almost no latency change. Record load level, completed throughput, and latency distribution simultaneously to avoid extrapolating the single-request model to all scenarios.
Form Verifiable Optimization Conclusions
First fix the input and completed work, then count, then sample to locate candidates; propose an explainable mechanism and modify it, then return to the same end-to-end metric for verification. Amdahl's Law reminds us that even if a part accounting for 10% of total time is completely eliminated, it can only reduce the original time by 10%.
If the hotspot proportion drops but request time does not change, the bottleneck may have shifted, the load may have changed, or the original metric may only cover part of the CPU. Preserve commands, machine information, symbol versions, and baselines so that "looks faster" becomes a reproducible conclusion.
Retain the Minimum Evidence Package for Each Conclusion
Preserve source code or build identifiers, binary build-id, complete commands, event modifiers, input scale, completed quantity, number of runs, and measurement environment. For sampling, additionally record the call stack method, data file, and loss/throttling status; for counting, additionally record event running ratio and unsupported items.
If the new version is only 1% faster than the baseline, and the fluctuation of repeated runs is larger than this, first improve the experiment or add representative repetitions; do not rush to attribute small differences to changes. You can alternate A/B execution to reduce interference from time drift, and report cold start and steady state separately. The final conclusion should land on "reduced time or resources by X under given load"; perf metrics are used to explain mechanisms.