ProductionAdvanced

The four meanings of 100% CPU

“A process is at 100% CPU. What are the different things that could mean, and how do you tell them apart in the first five minutes?”

What this tests

  • That 100% CPU has several distinct causes with different signatures
  • Reading user vs system time, per-thread usage, and syscall counts
  • Using two stack samples to find a loop; profilers for the hot function
  • Lock contention and GC as CPU consumers that are not "the code being slow"

Answers by level

Read the beginner answer first and notice what is missing.

At least five different things: (1) legitimately busy — throughput-bound work, correlated with load; (2) a hot or infinite loop in user code — a retry without backoff, a spin on a condition; (3) lock contention or busy-waiting — threads spinning or bouncing through futex; (4) a syscall storm — tiny reads and writes, stats, epoll_wait with zero timeout; (5) garbage collection or a runtime pathology — a heap near its limit collecting continuously. Each has a different fix, so the job is to classify before profiling (The OS Debugging Playbook: Four Symptoms, Fourteen Causes).

First, top: the us/sy split. High user time points at the application (1, 2, 5); high system time points at the kernel — syscalls or contention on kernel locks (3, 4). wa high means the process is waiting, not computing, whatever the load average says. Then top -H -p <pid> for per-thread usage: one thread at 100% and the rest idle is a loop or a single-threaded bottleneck; all threads at 100% with throughput *lower* than before is contention; N threads named GC at 100% is the collector.

Second, strace -c -p <pid> for ten seconds (or perf trace): a hot loop makes no syscalls; a syscall storm shows millions of read/write/stat; lock contention shows futex dominating; a busy event loop shows epoll_wait returning constantly. Third, two stack samples ten seconds apart — gdb -p + bt, jstack, py-spy dump: the same frame twice is a loop or a spin; different frames is real work. Only then a profiler or a flame graph to name the function (System Calls).

Remember what "100%" means: top reports per-process CPU as a percentage of one core, so 100% on a 16-core box is one core — a single-threaded bottleneck, not saturation. And a process can be *slow* without being at 100%: cgroup throttling, steal time on an oversubscribed VM (st in top), or frequency scaling look like slowness with spare CPU.

Green flags · Red flags

Strong green flag · Says "classify by us/sy, per-thread and syscall rate, then profile", and can predict what each class looks like.
Green flags
  • Lists at least four distinct causes before reaching for a profiler
  • Uses the user/system split and per-thread view to classify
  • Uses strace -c and two stack samples
  • Knows 100% is one core in top and recognises single-threaded bottlenecks
  • Mentions GC, contention or steal time
Red flags
  • Goes straight to optimising the hottest function
  • Cannot interpret system time
  • Does not know that a lock can consume CPU

Follow-up questions

F1
top shows 70% sy for your process and strace -c is all futex. What is it?
F2
One thread at 100%, no syscalls, same frame in two samples. What and why does the service still answer?
F3
CPU 40%, service slow, st at 25%. What?

Scenario

After a config change from 8 to 64 worker threads, a service pins all cores, throughput drops 40%, and perf top shows native_queued_spin_lock_slowpath. Explain what is happening and what you would change first.

Learn this topic