CPU Execution Provider
Question answered
How does the native CPU EP balance portability, correctness, SIMD performance, threading and memory?
The CPU EP is both a portable execution backend and the native runtime’s most accessible correctness baseline. It implements the shared Execution Provider Contract and registers kernels by ONNX domain/op type.
Backend strategy
ONNX node
↓ registry + shape/dtype checks
CPU kernel
├── portable Generic/reference path
├── built-in x86 SIMD path when available
└── MLAS-backed paths for selected operations/featuresThe portable path matters even when an optimized path exists:
- it runs without a vendor toolkit;
- it provides a correctness comparison;
- it prevents a missing ISA from becoming a runtime failure;
- it gives tests a simple mechanism to isolate optimized-path bugs.
Runtime capability, not build-machine identity
Fast paths should be selected from runtime CPU capabilities and tensor requirements. AVX-512/AVX2/NEON/SVE availability changes speed, not semantics. Unsupported instructions must degrade to a correct path.
Kernels are shape- and dtype-driven. Model names and fixed hidden dimensions do not belong in the EP.
Hot-path architecture
The CPU EP includes:
- blocked/register-tiled GEMM and SIMD backends;
- quantized matmul and MoE kernels;
- attention, normalization, indexing and data-movement kernels;
- EP-specific fusion/optimization passes;
- host parallelism, decode affinity and NUMA-aware support;
- weight-offload placement and host-cache mechanisms.
The session should observe a Kernel, not which internal GEMM implementation ran.
Threading lessons
Thread count is part of the algorithm:
- per-thread scratch multiplied by worker count can become a process-scale memory claim;
- nested parallelism can oversubscribe cores;
- decode often benefits from a bounded worker pool rather than all available hardware threads;
- NUMA placement can dominate arithmetic improvements for large weights;
- process affinity and runtime thread budgets are different controls.
Any resident per-thread or per-kernel buffer that scales with model weight or thread count must be planned in actual bytes and be declinable.
Persistent caches
CPU performance may use:
- transposed weight caches;
- dense/widened weight caches;
- quantized packed-B buffers;
- resident dequantized weights;
- reusable large host allocations;
- accumulator scratch pools.
A cache is a memory policy
If it outlives one kernel call and scales with weights or threads, it must be declared before allocation, charged by actual footprint and have a correct fallback when declined.
Correctness and performance gates
An optimization should preserve:
- output values within the justified tolerance;
- byte-identical token IDs where deterministic generation is expected;
- supported shapes/dtypes/opsets;
- explicit fallback behavior;
- bounded persistent memory;
- portability to machines without the fast ISA.
Kernel-vs-old-kernel speedup is insufficient evidence. Compare production shapes against the relevant ORT CPU EP or another strong baseline.
Formal sources
onnx-runtime-ep-cpudocs/performance/KERNEL_PERF.mddocs/performance/CPU_MATMUL_ASSIGNMENT.mdCPU EP vs ORT benchmarkdocs/architecture/CROSS_PLATFORM.md