Start by treating false sharing, lock contention, scheduler migration, and remote-NUMA access as separate hypotheses, then change one condition at a time. Treat a single hardware counter as suggestive, not conclusive. A practical diagnostic pattern is to hold workload constant, compare pinned and unpinned runs, try a data-layout change that preserves synchronization, and inspect scheduler or lock evidence alongside cache and memory measurements. If padding is the main change that helps, false sharing is a strong candidate; if affinity is the main change that helps, scheduling or memory placement are worth testing next; if time blocked on a lock dominates, investigate synchronization first.
What are you actually trying to separate?
These symptoms overlap because they all increase latency between cores. False sharing is different from lock contention: threads write different words that live on the same cache line, so ownership bounces even without a lock. Lock contention and true sharing both center on the same synchronization variable or protected data. NUMA traffic is different again: the line may not be bouncing between writers, but accesses pay remote-node cost. Scheduler migration can amplify all of them by moving runnable work between CPUs and disturbing cache locality.
That is why “high cache misses” or “lots of coherence traffic” is not enough by itself. You need an intervention that changes one cause while leaving the others alone.
What measurements should you trust first?
Start with a fixed workload and fixed thread count. Run it in two modes: unpinned, then pinned with stable CPU affinity. Linux kernel guidance on reducing per-CPU kthread jitter explicitly points to CPU binding tools such as taskset and sched_setaffinity(), and it shows using ftrace under /sys/kernel/tracing with function_graph to locate kernel-generated OS jitter on a CPU.
Then ask three questions in order:
- Does your runtime or profiler show substantial time blocked on a lock? If yes, investigate that before cache-line theories.
- Does pinning materially change runtime variance or throughput? If yes, scheduler migration or memory placement is in play.
- Does changing only data layout help while synchronization stays identical? That makes false sharing a stronger candidate.
In the Linux kernel, workqueue behavior can also reflect CPU locality. Linux workqueue documentation describes concurrency-managed workqueues as using per-CPU worker-pools, and says that unless specifically overridden, a bound workqueue queues work on the worker-pool associated with the CPU the issuer is running on. That is a useful reminder to check whether your execution model itself is reinforcing locality assumptions during measurement.
How do you run a clean false-sharing test?
Worked example: each thread increments its own counter in a shared array.
Version A stores counters adjacently in memory. Version B pads each counter so separate threads land on different cache lines. Keep the loop count, thread count, and synchronization identical. Do not also change batching, locks, or work partitioning.
Decision checklist:
- A slows badly, B improves, and lock-blocked time stays flat: false sharing is a strong candidate.
- A and B are similar, but pinning helps: migration or NUMA placement are worth testing next.
- A and B are similar, but lock wait dominates: lock contention is the better first hypothesis.
- First-touch memory on the wrong NUMA node hurts both versions similarly until you bind memory near the workers: remote NUMA access is a strong candidate.
What commonly produces a false conclusion?
Padding can fail because the allocator still places hot objects together, or because the structure contains another shared field on the same line. Pinning can also hide production behavior: if your service normally migrates under load, a pinned microbenchmark may prove the mechanism without reflecting deployed performance. And a “successful” rewrite may accidentally reduce synchronization frequency, which means you changed two variables at once.
Follow-up Q&A
Q: Can hardware counters confirm false sharing by themselves?
No. They can support the diagnosis, but a controlled before-and-after layout change, with the same workload and synchronization, is usually more persuasive than counters alone.
Q: If pinning fixes it, is the root cause definitely scheduling?
Not definitely. Pinning may also improve NUMA locality or reduce line-bouncing distance. Follow with a memory-placement test and a data-layout test before deciding.
Next step: build the smallest reproducer that preserves your access pattern, then run the four-way matrix: original versus padded, and pinned versus unpinned.
Reviewed: 2026-09-06.
Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗