With perf top On Linux, I can quickly identify which kernel functions are currently consuming the most CPU time and where bottlenecks are occurring. In this guide, I’ll walk you through clear steps to help you identify live hotspots, interpret the output accurately, and use that information to make quick optimizations for the scheduler, network, and memory.
Key points
I think the live view of perfect It's great for getting off to a strong start because it immediately highlights the biggest time wasters. The percentages shown for each icon tell me whether the bottleneck is in the Kernel or is located in user space. I use recurring patterns to determine whether locks, IRQs, the network, or memory are the dominant factors. I then narrow down the hotspot using more in-depth tools and test changes directly under load. This is how I improve the CPU-Optimize utilization and reduce latency in a sustainable manner.
- Live Hotspots Identify and prioritize
- Percentages Interpret correctly for each function
- Core Focus Set: IRQs, locks, memory
- Workflow: top → record → report
- Optimizations verify in a targeted manner
What is Perf Top, and what is it used for?
I use perf top, to immediately see which symbols are consuming the largest share of CPU time while a load is running. The tool accesses hardware performance counters and shows me an updated ranking of the most resource-intensive functions at short intervals. According to Linux-Magazin, perf handles both profiling and tracing, which makes the Live View seamlessly integrates with more in-depth analyses. In my usual workflow, I supplement the snapshot with `perf record` and `perf report` to examine call graphs and exact paths. This is how I answer the key question: Where does the CPU Is it currently running—in the network stack, the memory subsystem, the scheduler, or a driver?
Install and start perf top
After installing it using the distribution package, I run perfect top typically runs with elevated privileges so that kernel symbols and system events are visible. Simply running „perf top“ is enough to set up an initial live view and identify dominant functions. If I need to focus on individual processes, I attach the PID I start it with -p; for specific CPUs, I use -C with a list or a range. I specify events using -e, such as cpu-cycles, instructions, or branch-misses, depending on the question I want to answer. To ensure reproducible results, I start the measurement during an actual load so that Hotspots be clearly audible and not get lost in background noise.
Here's how I read this issue correctly
In the list, I first evaluate the Percentages per symbol, because they reflect the relative time shares. High proportions for system functions indicate a bottleneck in the kernel, while dominant userspace symbols tend to point to application logic. If I see many scheduler routines, I suspect too many active threads, unfavorable affinities, or inappropriate priorities. If memory functions appear at the top, I check allocation patterns, page faults, NUMA locality, and caches. For network paths, I look at IRQ distribution, Gro/TSO settings, and driver behavior, because such details reveal the Latency have a significant impact on.
Common Causes of Kernel Hotspots
Many hotspots arise because many small costs add up to a large Load add up. Excessive context switches, lock contention, and unevenly distributed IRQs often increase CPU time. Similarly, fragmented memory structures, inefficient slab usage, or constant paging consume unnecessary cycles. If a particular driver stands out, I correlate it with the workload, hardware, and version to narrow down potential side effects. On multicore systems, I also check for false sharing, because according to the kernel documentation, shared cache lines can quickly lead to expensive Overhead can cause concern.
Sample Analysis of a Hotspot
If I see consistently high percentages of network-related functions over an extended period, the first thing I do is break down the traffic types: small vs. large packets, TLS vs. plain text, many connections vs. a few long-lived sessions, in order to Cause narrow it down. Then I dig deeper using `perf record` and `perf report`, enable call graphs (-g), and compare the paths across multiple runs. If memory management turns out to be the issue instead, I check the allocator, huge pages, THP settings, and NUMA affinity, because unnecessary paths can quickly arise here. I often interpret scheduler hotspots as a sign of too many runnable threads or inappropriate CPU binding. I always change only one Parameters per run, so I can accurately track the effect.
The best choice for hosting environments
In hosting scenarios, I often see how small kernel costs can Latency The combined impact of many services. Containers, VMs, and database instances running in parallel significantly shift the profile toward network, storage, and the scheduler. Using `perf top`, I can determine whether the bottlenecks lie primarily in IRQ handling, soft IRQ processing, or lock paths. I then factor in the kernel version, NUMA layout, IRQ affinities, and queue depths into the analysis, since these factors interact with one another. If you want to dive deeper, you’ll find this guide to Analyzing CPU Bottlenecks Other practical approaches that I regularly use in my work.
Practical Guide to Analysis
I'll start with a reproducible load scenario so that the measurements remain comparable and Hotspots appear consistently. Then I run `perf top` and note the dominant symbols over several updates. I use `perf record/report` to consolidate this snapshot into a clear picture of the call graphs so I can identify the path to the bottleneck. Next, I make a targeted change to just one thing—for example, an IRQ affinity or a queue depth—and measure again. Only once the effect is clear do I move on to the next Step I review and document the findings for future maintenance windows.
When Other Tools Are Useful
For historical views, more detailed call graphs, or specific event chains, I use perfect record/report, ftrace, or eBPF. Tracepoints help me shed light on specific paths, while BPF programs provide me with flexible metrics. If I want to look deeper into kernel paths, eBPF Analysis Tools Valuable signals right where the action is. For caching and sharing issues, perf-c2c and pahole are helpful once the hotspot has been clearly identified. This is how I narrow down the analysis from the live view to the root cause without getting bogged down in irrelevant Details to lose.
Sampling Options and Filters in Practice
I'll pass on the Sampling- I tailor the strategy to the specific issue rather than measuring everything indiscriminately. When sporadic spikes occur, I increase the sampling frequency and shorten the display intervals to capture fleeting spikes. For process-focused profiling, I set -p to the relevant PID; for CPU-focused profiling, I set -C to the hot cores. With -e, I control the event type—for example, `cpu-cycles` for broad profiling or `cache-misses` when memory behavior is suspected. I use call graphs (-g) as soon as I’ve roughly localized a hotspot and the Cause wants to find in the stack.
The following table shows practical switch combinations that I often use in my daily work, along with typical applications for each option:
| Option | Effect | Use |
|---|---|---|
| -p PID | Limits measurement to a single process | App-specific Hotspots narrow down |
| -C CPU List | Focus on Selected Cores | Check NUMA/IRQ Distribution |
| -e event | Select a hardware or software event | cycles, instructions, cache misses |
| -g | Enables call graph sampling | Expensive Paths in the Stack recognize |
| –kernel/–user | Filters by kernel or user space | Break Down the Source of CPU Time |
| –sort | Sorted by Symbol, DSO, dso:symbol | Readability of the Rankings increase |
I always test configurations briefly before starting longer measurements, so that the Display remains stable and no side effects occur. Especially at high sampling frequencies, I pay close attention to overhead to avoid placing an unnecessary burden on the system. For container hosts, I also check whether namespace and cgroup limits restrict visibility. To ensure reproducible benchmarks, I document all settings, including kernel and driver versions. This discipline saves me a lot of trouble later on Time in assessing changes.
Subsystem Interpretation: Network, Storage, Scheduler
If network paths are listed at the top, I first check IRQ affinities, RSS (Receive-Side Scaling), and offloads such as GRO/TSO, because these tuning parameters affect the Throughput-Adjust the latency balance. When memory behavior is unusual, I look at allocation patterns, huge pages, slab statistics, and page fault rates. I often associate scheduler load with excessive thread counts, missing CPU affinity, or unfair prioritization. For specific kernel events, I also set tracepoints or use bpftrace in hosting, to confirm hypotheses. This is how I combine the live observations from perf top with data from deeper measurement points and arrive more quickly at the actual Cause.
Requirements and Visibility of Icons
So that perf top When resolving all relevant kernel symbols, I pay attention to two things: appropriate permissions and available symbol information. On production systems, kernel.perf_event_paranoid often set high. For deep kernel views, I temporarily reduce this value or work as root with the necessary privileges (CAP_PERFMON/CAP_SYS_ADMIN). If kernel addresses are masked (kptr_restrict), I usually still see names, but not raw addresses—that's enough for me to prioritize. For user space, I install the corresponding debug info packages so that `perf top` shows function names instead of offsets. This reduces guesswork and speeds up the process of finding the root cause.
Percentages and Sampling Pitfalls
I interpret the percentages in the list as relative shares of the measured samples, not as the exact CPU utilization over time. If I select multiple events, I can Multiplexing Take action: Perf spreads the counters out over time and normalized the display. To get a clear picture, I first measure broadly using CPU cycles or instructions and add special events later. I capture short-term spikes using a higher frequency (-F) and shorter intervals; for idle systems, the default frequency is sufficient. I also note that Idle-Phase and frequency changes (Turbo, Governor) can distort the results. For comparative measurements, I therefore standardize the clock and power settings.
Call Graphs in Depth
Once I've identified a hotspot, I use call graphs to gain deeper insights. With -g and a suitable unwinding method, I can determine the path to the expensive section. Frame pointers or DWARF unwinding provide me with stable stacks; where available, I use hardware-assisted return buffers (LBR) for very precise call chains. I increase the mmap buffers only as much as necessary to keep overhead low. If the stack shows many helper functions, I pay attention to including vs. exclusive Costs: The key factor is whether the function itself is expensive or whether it merely serves as a transit path. This distinction often saves me hours of troubleshooting.
Working in Containers and VMs
In container environments, I check whether my view of cgroups and that the namespaces are correct. I focus my measurements on the relevant PIDs and CPUs so that noisy neighbors don't skew the results. For VMs, I check whether the virtual PMU is enabled; otherwise, I’ll be missing precise hardware events and will primarily see software signals. I can often identify KVM hosts by symbols around kvm_vcpu or vmx/svm. In such scenarios, I keep host and guest analyses clearly separate so that I don't confuse cause and effect.
Recognizable Patterns and Quick Hypotheses
In everyday life, certain patterns have proven effective, and I check them right away:
- Lock Competition: Scuba Diving queued_spin_lock_slowpath or mutex_spin_on_owner At the top level, data structures are too coarse-grained or work queues are too narrow. I reduce contention through sharding, finer lock granularity, or adjusted batch sizes.
- Scheduler Printout: Are piling up schedule(), pick_next_task_fair or wake-up paths, I adjust the number of threads, affinities, and priorities. Often, it’s enough to rein in “chatty” threads or clearly define CPU settings.
- Network SoftIRQs: Peaks at net_rx_action, napi_poll Or checksum offloads may indicate packet storms or suboptimal RSS and IRQ allocation. I assign IRQs to appropriate cores and adjust GRO/TSO to achieve the desired throughput/latency profile.
- Storage Paths: A lot of time in do_page_fault, copy_user_* or slab functions allow me to check allocation patterns, THP/Huge Pages, and NUMA locality. Incorrect placement here costs a great many cycles without being noticed.
- RCU and Timers: Dominate rcu_core or timer callbacks, I'm rethinking the polling and batch strategies for my services to make the system run more smoothly.
Deepen Your Understanding of Measurement Discipline and Reproducibility
To ensure clean, comparable runs, I keep environmental factors constant: CPU governor, turbo states, background jobs, and even room temperature for dense nodes. I assign test loads to specific cores and, if necessary, isolate “hot” CPUs to ensure that scheduler decisions remain stable. I document all changes, including kernel, driver, and firmware versions. For riskier tweaks, I plan fallback points and measure performance again immediately after making the change. This way, I obtain reliable Before/After-A story that I can still relate to even months later.
Practical Tips: Commands I Use Often
Depending on the question, I use concise formulas:
- Wide Scoping Under Load: perf top -e cpu-cycles –kernel –user
A quick overview of whether the kernel or user space is in control. - Process Focus with Callgraph: perf top -p PID -g –kernel –user
Show me live traces of the relevant application, without system noise. - CPU Focus: perf top -C 2-5 -e cpu-cycles -g
Helps with NUMA or IRQ hotspots when only a few cores are “overheating.”. - Suspected Storage: perf top -e cache-misses -e cycles -g –kernel
Displays memory paths relative to cycles. - Pin Down Loose Spikes: perf top -F 999 -I 1000 -e cycles
A higher frequency and shorter display intervals capture short peaks.
Interpretation Guidelines for Specific Subsystems
At Network In addition to NAPI and RX/TX paths, I also monitor TLS/crypto components, which can dominate when there is a high volume of handshakes. I check whether zero-copy or coalescing is effective and whether large segments (TSO/GSO) exceed my latency budgets. In the Memory- In this area, I check THP: Does it help my load, or do split/merge events cause interference? When Storage I interpret blk_mq-Symbols and io_uring paths as indicators of queue depths and merge strategies. When scheduler I link wake-up avalanches with lock or IO chains and balance the paths using backpressure instead of “more threads.”.
The Limits of Perf Top and When I Switch Tactics
Because perf top Since it’s sampling-based, I can see average patterns more clearly than individual events. For deterministic execution chains, I switch to tracepoints, ftrace, or eBPF to establish precise causal relationships. If I need exact quantification (e.g., instructions per request), I combine these with perfect stat or offline analyses from perf record/report. If I encounter unclear stacks (missing symbols, incorrect unwinding), I first fix the visibility—anything else would be like groping in the dark.
Briefly summarized
With perf top I can see in real time where the CPU is spending time in the kernel and which symbols I should investigate first. Based on the percentage values, recurring patterns, and the separation between kernel and user space, I determine specific next steps. Then I consolidate the findings using `perf record/report`, verify changes under load, and document my measurement chain. This approach pays off particularly well in hosting environments because many services and containers benefit from one another as soon as kernel paths run more efficiently. Those who internalize this process save days of troubleshooting and reduce Latencies and achieves noticeably more stable response times under real-world load.


