CPU Pinning & NUMA Topology
Complete guide to CPU pinning strategies and NUMA topology configuration for optimal VM performance
CPU Pinning & NUMA Topology
Introduction
CPU pinning maps vCPUs to specific physical CPU cores for better performance. Infinibay implements BaseCpuPinningStrategy (abstract base), BasicStrategy (NUMA-aware distribution), and HybridRandomStrategy (randomized load balancing).
System reads NUMA topology from /sys/devices/system/node/ and distributes vCPUs across nodes for local memory access.
Why CPU Pinning Matters
Performance: Reduces cache thrashing, improves cache locality, eliminates migration overhead. Typical improvement: 10-30% for CPU-intensive workloads.
NUMA: Local memory access 2-3x faster than remote. Avoids cross-socket QPI/UPI latency.
Guest OS: Proper CPU topology helps OS schedule threads correctly.
CPU Pinning Architecture
Strategy Pattern
File: backend/app/utils/VirtManager/CpuPinning/BasePinningStrategy.ts
BaseCpuPinningStrategy: Abstract base classBasicStrategy: NUMA-aware proportional distributionHybridRandomStrategy: Randomized distribution
CpuPinningConfig Interface
Defines cputune (vcpupin mappings), cpu (mode, topology, cache, maxphysaddr, NUMA cells).
Base Class
Provides: Read NUMA topology from /sys/devices/system/node/, expand CPU lists ("0-3,5" → array), create config objects, add topology/cache/maxphysaddr. Abstract method: setCpuPinning(vcpuCount).
Host NUMA Topology Detection
getNumaTopology: Reads /sys/devices/system/node/, filters node directories, reads cpulist files, expands to arrays.
getHostNumaMemory: Reads /sys/devices/system/node/nodeX/meminfo, parses MemTotal, converts kB to MiB.
expandCpuList: Expands "0-3,5,7-9" → ["0","1","2","3","5","7","8","9"].
BasicStrategy - NUMA-Aware Pinning
File: backend/app/utils/VirtManager/CpuPinning/BasicStrategy.ts
NUMA-aware, proportional distribution. Falls back to round-robin when NUMA not available.
setCpuPinning: Gets topology, checks if NUMA optimization possible, calls optimizeNumaPinning() or optimizeRoundRobinPinning().
canOptimizeNumaPinning: Returns true if topology has at least one node with CPUs.
optimizeNumaPinning Method
Calculates proportional vCPU distribution across nodes, assigns vCPUs to physical CPUs (uses modulo for oversubscription), generates NUMA cells with memory, adds topology/cache/maxphysaddr.
optimizeRoundRobinPinning Method
Distributes vCPUs round-robin across all CPUs. Uses modulo for oversubscription. Creates single NUMA cell.
generateNumaCells Method
Validates VM memory ≤ host memory, calculates proportional vCPU/memory distribution, creates NUMA cells with vCPU ranges and memory allocation.
HybridRandomStrategy - Randomized Distribution
File: backend/app/utils/VirtManager/CpuPinning/HybridRandom.ts
Shuffles NUMA nodes and CPUs for load balancing. Better for multiple VMs.
setCpuPinning: Shuffles nodes (Fisher-Yates), shuffles CPUs within nodes, assigns vCPUs to shuffled CPUs, generates NUMA cells, adds topology/cache/maxphysaddr.
generateNumaCells: Sorts vCPUs, formats as compact ranges ("0-2,5,7-8"), calculates memory proportionally.
formatCpuRanges: Groups consecutive CPUs into ranges.
shuffleArray: Fisher-Yates shuffle for uniform distribution.
CPU Configuration
addCpuTopology: Default 1 socket/N cores. For 4+ vCPUs (divisible by 2): 2 sockets/N/2 cores. CPU mode: host-model.
addCpuCache: L3 cache, emulate mode.
addCpuMaxPhysAddr: 42 bits, emulate mode.
Helpers
getHostCpuInfo: Reads /proc/cpuinfo for model, vendor, feature flags.
createCpuPinningConfig: Creates config with vcpupin elements.
addNumaToConfig: Adds NUMA cells to config.
XMLGenerator Integration
setCpuPinningOptimization (xmlGenerator.ts): Default HybridRandomStrategy, gets vCPU count from XML, calls setCpuPinning(), applies cputune and cpu config to domain XML.
Examples
4 vCPUs, 2 nodes (BasicStrategy): vCPU 0-1 → node0, vCPU 2-3 → node1. 2 NUMA cells, 2 sockets/2 cores.
12 vCPUs, 1 node (oversubscription): vCPU 0-7 → CPU 0-7, vCPU 8-11 → CPU 0-3 (shared). 1 NUMA cell.
HybridRandomStrategy: Shuffles nodes/CPUs for randomized assignment.
Troubleshooting
Guest OS doesn't detect cores: Check topology in XML, NUMA cells match vCPU count. Use lscpu (Linux) or Task Manager (Windows).
Poor performance: Check NUMA locality (numastat -p <pid>), verify pinning (virsh vcpupin), avoid oversubscription.
NUMA not detected: Check /sys/devices/system/node/ exists, NUMA enabled in BIOS, kernel support (dmesg | grep NUMA).
Mermaid Diagrams
CPU Pinning Strategy Selection
flowchart TD
Start([XMLGenerator.setCpuPinningOptimization]) --> CheckStrategy{Strategy<br/>provided?}
CheckStrategy -->|Yes| UseProvided[Use provided strategy]
CheckStrategy -->|No| CreateDefault[Create HybridRandomStrategy]
UseProvided --> GetVCPU[Get vCPU count from domain XML]
CreateDefault --> GetVCPU
GetVCPU --> CallStrategy[Call strategy.setCpuPinning]
CallStrategy --> ReadNUMA[Strategy: Read host NUMA topology]
ReadNUMA --> CheckOptimize{Can optimize<br/>NUMA?}
CheckOptimize -->|BasicStrategy + Yes| OptimizeNUMA[optimizeNumaPinning]
CheckOptimize -->|BasicStrategy + No| RoundRobin[optimizeRoundRobinPinning]
CheckOptimize -->|HybridRandomStrategy| ShuffleNUMA[Shuffle NUMA nodes]
ShuffleNUMA --> ShuffleCPUs[Shuffle CPUs within nodes]
ShuffleCPUs --> AssignShuffled[Assign vCPUs to shuffled CPUs]
OptimizeNUMA --> GenCells[Generate NUMA cells]
RoundRobin --> GenCells
AssignShuffled --> GenCells
GenCells --> AddTopology[Add CPU topology<br/>sockets, cores, threads]
AddTopology --> AddCache[Add CPU cache<br/>L3, emulate]
AddCache --> AddMaxPhys[Add maxphysaddr<br/>42 bits]
AddMaxPhys --> ReturnConfig[Return CpuPinningConfig]
ReturnConfig --> ApplyCputune[XMLGenerator: Apply cputune]
ApplyCputune --> ApplyCPU[XMLGenerator: Apply cpu config]
ApplyCPU --> LogConfig[XMLGenerator: Log configuration]
LogConfig --> End([End])
NUMA-Aware Pinning Process
sequenceDiagram
participant BasicStrategy
participant FileSystem
participant XMLGenerator
BasicStrategy->>FileSystem: Read /sys/devices/system/node/
FileSystem-->>BasicStrategy: node0, node1 directories
BasicStrategy->>FileSystem: Read node0/cpulist
FileSystem-->>BasicStrategy: "0-7,16-23"
BasicStrategy->>FileSystem: Read node1/cpulist
FileSystem-->>BasicStrategy: "8-15,24-31"
BasicStrategy->>BasicStrategy: Expand CPU lists
BasicStrategy->>BasicStrategy: Calculate vCPUs per node (proportional)
BasicStrategy->>BasicStrategy: Assign vCPUs to physical CPUs
BasicStrategy->>FileSystem: Read node0/meminfo
FileSystem-->>BasicStrategy: MemTotal: 67108864 kB
BasicStrategy->>FileSystem: Read node1/meminfo
FileSystem-->>BasicStrategy: MemTotal: 67108864 kB
BasicStrategy->>BasicStrategy: Calculate memory per NUMA cell
BasicStrategy->>BasicStrategy: Generate NUMA cells
BasicStrategy->>BasicStrategy: Create CpuPinningConfig
BasicStrategy->>BasicStrategy: Add topology, cache, maxphysaddr
BasicStrategy-->>XMLGenerator: Return CpuPinningConfig
NUMA Topology Structure
graph TD
Host[Host System]
Host --> Node0[NUMA Node 0]
Host --> Node1[NUMA Node 1]
Node0 --> CPUs0[CPUs 0-7,16-23]
Node0 --> Mem0[Memory 64GB]
Node1 --> CPUs1[CPUs 8-15,24-31]
Node1 --> Mem1[Memory 64GB]
Guest[Guest VM]
Guest --> GCell0[Guest NUMA Cell 0]
Guest --> GCell1[Guest NUMA Cell 1]
GCell0 --> vCPUs0[vCPUs 0-3]
GCell0 --> GMem0[Memory 4GB]
GCell1 --> vCPUs1[vCPUs 4-7]
GCell1 --> GMem1[Memory 4GB]
vCPUs0 -.Pinned.-> CPUs0
vCPUs1 -.Pinned.-> CPUs1
GMem0 -.Allocated from.-> Mem0
GMem1 -.Allocated from.-> Mem1