Core: Read the cache topology from sysfs on Linux

queryCacheSizes() reports 0/0/0 on every non-x86 Linux, so GEMM there blocks against the compiled-in default sizes rather than the hardware.

The POSIX branch added in !2282 (merged) calls sysconf(_SC_LEVEL*_CACHE_SIZE), but glibc answers those from CPUID and so only implements them on x86. Confirmed on three aarch64 machines (Jetson Orin, Grace, DGX Spark): all return 0. This adds a sysfs fallback, which Linux populates on every architecture. EIGEN_NO_CPU_CACHE_SYSFS opts out.

Feeding the heuristic a real L2 then exposes a constant fitted to the placeholder: actual_l2 = 1.5 * l2 was calibrated against a 1MB modeled L2, so a core whose real L2 is 256KB gets a quarter of the intended rhs panel. Where sysfs describes the L3, one CPU's slice of it is used as a floor for the rhs-panel budget instead. Both halves of that slice come from the same sysfs entry: sysconf reports the package total (128MB on a 32-core Threadripper whose cores each reach one 16MB slice), so dividing it by a single instance's sharer count would overstate the share eightfold.

The slice lives in CacheSizes alongside l1/l2/l3, so setCpuCacheSizes() clears it and EIGEN_DEBUG_SMALL_PRODUCT_BLOCKS zeroes it — callers force small cache sizes precisely to exercise multi-pass blocking. It feeds only the rhs-panel budget, deliberately not actual_lm, whose blockA allocation tuning is separate.

Measured

machine detection before → after ST N=2048 float
Grace (Neoverse V2) 0/0/0 → 64K/1M/114M 87.8 → 88.3
Jetson Orin (A78AE) 0/0/0 → 64K/256K/2M 24.87 → 24.06
DGX Spark (X925) 0/0/0 → 64K/512K/8M 155.3 → 158.1
Threadripper 3975WX / 3960X unchanged (sysconf works) 80.3 → 81.0 / 82.5 → 83.3
Apple M4 unchanged (sysctl answers, no sysfs) —

Multithreaded blocking is bit-identical wherever detection already worked; only the single-threaded branch uses the slice.

The Jetson loss is the one regression and is specific to a core whose real L2 is far below the 1MB placeholder — Grace, at exactly 1MB, is neutral. Recalibrating the rest of the heuristic for small-L2 parts wants data from more ARM parts and is left to a follow-up.

Tested

product_large, product_small, product_notemporary, tensor_contraction on Grace, DGX Spark, Threadripper PRO 3975WX, and macOS M4. The new assertion fails at the parent commit on aarch64 Linux.

Review round 2

  • Every CPU the thread may run on, not CPU 0 (19704125). The reader takes the sysfs root and a CPU; the query merges every CPU the calling thread's affinity mask admits, keeping per level the smallest cache any of them reports. Where the mask is unavailable, every online CPU is a candidate. A cpu-list range walker replaces the bare counter and rejects negative or impossible ids. Cost: 3ms for 32 CPUs on WSL2 (85us for one CPU), once per process.
  • sysfs independent of the GNU sysconf constants (3c2ab3d0). Only the sysconf calls are guarded by _SC_LEVEL1_DCACHE_SIZE; the sysfs pass runs on every Linux and supplies the share through a four-argument queryCacheSizes(), so CacheSizes reads all four numbers from one detection. Verified under musl in an Alpine 3.20 container with EIGEN_NO_CPUID forcing the non-x86 path: parent -1/-1/-1, now 48K/1M/32M + 1M share.
  • The cache-detection coverage moves to a dedicated cache_sizes test: parsers, a big.LITTLE sysfs fixture (masks confined to the big cores, excluding CPU 0, hitting a CPU without a cache directory, a malformed or missing online list), and a host check that confines the thread to every allowed CPU but 0 and verifies the production query follows the mask. The setCpuCacheSizes() override check stays in product_large.

Tested this round on x86-64 Linux (WSL2, GCC 13): cache_sizes, all product_large/product_small/product_notemporary parts (46/46), cache_sizes under taskset -c 1-3 and -c 5, and cache_sizes under musl. Not re-run on the ARM machines: the per-CPU minimum equals the CPU-0 reading on a homogeneous part, so their numbers above stand, but a Grace/Jetson run of cache_sizes would confirm.

Edited by Rasmus Munk Larsen

Merge request reports

Loading
Loading