CPU affinity controls which processor cores execute specific processes on a Linux system. On multi-core servers running database engines, web servers, or real-time applications, improper CPU affinity can cause cache thrashing, NUMA penalties, and unpredictable latency. This guide compares three tools for managing CPU affinity on self-hosted Linux servers: taskset (process-level pinning), numactl (NUMA-aware placement), and cset (advanced CPU set management with shielding).
What Is CPU Affinity and Why Does It Matter?
Linux schedules processes across all available CPU cores by default. This works well for general workloads but causes problems for performance-sensitive applications:
- Cache locality: When a process migrates between cores, its L1/L2 cache state is lost, causing cache misses on the new core
- NUMA penalties: Accessing memory from a remote NUMA node adds 40-100ns latency per access
- Context switch overhead: Migrating processes between cores requires TLB flushes and cache invalidation
Proper CPU affinity pinning keeps critical processes on dedicated cores, maintaining cache warmth and eliminating cross-NUMA memory access penalties.
| Feature | taskset | numactl | cset |
|---|---|---|---|
| Granularity | Per-process | Per-process + NUMA | CPU set-based |
| NUMA Awareness | No | Yes | Yes |
| Shielding | No | No | Yes (kthread migration) |
| Ease of Use | Simple (single command) | Simple (single command) | Moderate (setup required) |
| Persistence | Per-launch | Per-launch | Persistent sets |
| Container Support | Manual | Manual | cset shield with Docker |
| GitHub Repo | util-linux (3,154 stars) | numactl (496 stars) | cpuset (133 stars) |
| Last Updated | 2026-05-20 | 2026-02-24 | 2025-04-08 |
taskset — Simple Process-Level CPU Pinning
taskset is part of the util-linux package and provides the simplest way to set or retrieve CPU affinity for running processes. It accepts CPU masks in hexadecimal or list format.
Installation
| |
Basic Usage
| |
Hexadecimal CPU Masks
| |
Docker Compose with taskset
You can integrate taskset into container startup commands:
| |
numactl — NUMA-Aware Process Placement
numactl controls NUMA policy for processes, allowing you to specify which NUMA node a process runs on AND where its memory is allocated. This is critical for multi-socket servers where cross-node memory access significantly impacts performance.
Installation
| |
Checking NUMA Topology
| |
Running Processes with NUMA Policy
| |
Docker Compose with numactl
| |
cset — Advanced CPU Set Management with Shielding
cset (CPU set) is a Python-based tool that provides high-level CPU set management. Its key feature is shielding — it moves all kernel threads and non-critical processes off designated CPU cores, creating a clean environment for performance-critical workloads.
Installation
| |
Creating CPU Shields
| |
Docker Integration with cset
| |
cset vs taskset: Key Differences
While taskset pins individual processes to specific CPUs, cset creates isolated CPU sets where:
- Kernel threads are automatically migrated away from shielded cores
- System daemons are excluded from the shield
- Multiple processes can run inside the shield without individual pinning
- Memory policy can be combined with CPU shielding
Why Self-Host and Manage CPU Affinity Locally?
Managing CPU affinity on your own bare-metal or dedicated servers gives you control over process placement that cloud instances often restrict. Virtual machines in cloud environments typically have their vCPU scheduling managed by the hypervisor, making fine-grained affinity settings less effective.
For database servers, pinning the database process to specific CPU cores ensures consistent cache behavior and eliminates scheduler-induced latency variance. For real-time applications (trading systems, audio processing, industrial control), CPU shielding prevents background tasks from causing timing jitter on critical cores.
For related performance optimization, see our Linux I/O Schedulers guide for disk I/O tuning and Linux HugePages management for NUMA-aware memory optimization. Our interrupt management guide covers the complementary topic of hardware interrupt placement.
Choosing the Right CPU Affinity Tool
| Scenario | Recommended Tool |
|---|---|
| Simple process pinning | taskset |
| Multi-socket server with NUMA | numactl |
| Real-time/low-latency workloads | cset shield |
| Database server on NUMA hardware | numactl with –cpunodebind and –membind |
| Container orchestration | taskset in container entrypoint |
| Kernel thread isolation | cset shield with –kthread=on |
FAQ
What is the difference between taskset and numactl?
taskset controls which CPU cores a process runs on but has no awareness of NUMA topology. numactl controls both CPU placement AND memory allocation across NUMA nodes. On a single-socket system, taskset is sufficient. On multi-socket servers, numactl is essential to avoid cross-NUMA memory access penalties.
Does CPU affinity persist after a process restart?
No. CPU affinity set via taskset or numactl applies only to the launched process. If the process crashes and restarts, it returns to the default scheduler behavior. To persist affinity settings, use a systemd service unit with CPUAffinity= or NUMAPolicy= directives, or wrap the process launch in a startup script.
Can I pin containers to specific CPUs?
Yes. Docker supports CPU pinning via --cpuset-cpus flag: docker run --cpuset-cpus="0-3" myimage. In Docker Compose, use deploy.resources.limits.cpus combined with a taskset wrapper in the entrypoint. Kubernetes uses resources.limits.cpu and node affinity for broader placement control.
What is CPU shielding and why is it useful?
CPU shielding (provided by cset) moves all non-essential processes and kernel threads off designated CPU cores, creating an isolated environment for performance-critical workloads. This eliminates cache pollution from background tasks and prevents kernel thread interrupts on shielded cores. It is especially useful for real-time trading, audio processing, and industrial control systems.
How do I verify CPU affinity is working?
Use taskset -cp <PID> to check a running process affinity. Use numactl --show inside the process to verify NUMA policy. For cset shields, use cset shield --status to see which processes are running inside the shield. The /proc/<PID>/status file also shows Cpus_allowed_list for any process.
Does CPU affinity affect power consumption?
Yes. Pinning processes to fewer cores can allow unused cores to enter deep sleep states (C-states), reducing power consumption. However, concentrating load on fewer cores may increase their frequency (turbo boost), potentially increasing per-core power draw. The net effect depends on your workload and CPU architecture.
Can I use taskset and numactl together?
Yes, but it is redundant. numactl includes CPU placement functionality (--cpubind), so you do not need taskset when using numactl. Use numactl --cpubind=0-3 --membind=0 ./app instead of chaining taskset and numactl together.