← Concepts

阅读中文版本

Lock Contention Profiling

Lock contention profiling finds the locks where threads wait, so the optimization targets the lock that bounds throughput rather than the lock that is held but never contended.

performance

Definition

Lock contention profiling is the measurement of the time threads spend waiting to acquire locks, identifying the specific locks and call sites where contention bounds throughput, so that the optimization — sharding, lock-free, narrower critical section — targets the contended lock rather than the held-but-uncontended one.

Context and scope

Applied in multithreaded performance tuning, using profiler contention views (perf, JFR, pprof) to find the bottleneck lock.

Common misconceptions

  • A lock that is held a long time is the bottleneck — it is the bottleneck only if other threads want it; a long-held uncontended lock costs nothing
  • Reducing critical section time always helps — it helps the contended lock; for an uncontended lock it adds complexity for no gain

Anti-patterns

  • Optimizing a lock that the profiler shows is held often but never waited on, paying complexity for no throughput gain
  • Reducing the critical section by moving work out, but moving it to a second contended lock and just moving the bottleneck

Related concepts

Authoritative source

https://github.com/brendangregg/perf-tools

What it is

Lock contention profiling is the measurement of the time threads spend waiting to acquire locks, identifying the specific locks and call sites where contention bounds throughput, so that the optimization — sharding, lock-free, narrower critical section — targets the contended lock rather than the held-but-uncontended one. Applied in multithreaded performance tuning, using profiler contention views (perf, JFR, pprof) to find the bottleneck lock. Performance is a distribution, not a number: the tail determines user experience, and the mean hides the outliers that cause abandonment.

How it works

Measurement targets percentiles at the workload’s actual concurrency, not a single sequential run. The fast path is kept fast by deferring work that is not on it — lazy initialization, coalescing writes, avoiding allocation in hot loops. Profiling finds the bottleneck; micro-optimization without a profile optimizes the wrong thing. Constant factors matter, but algorithmic complexity dominates as scale grows.

When to optimize and pitfalls

Optimize when a measurement shows a concrete cost — latency budget burn, compute spend, user-visible jank. The pitfall is optimizing for the benchmark rather than the workload: a synthetic test that does not reproduce the production access pattern produces a local maximum that hurts globally. Another is trading memory for speed without accounting for the GC or eviction cost the extra allocation introduces.