RISC-V: cache-aware GEMM blocking (get_L2_size + blas_set_parameter)#5909
Open
hmeiland wants to merge 1 commit into
Open
RISC-V: cache-aware GEMM blocking (get_L2_size + blas_set_parameter)#5909hmeiland wants to merge 1 commit into
hmeiland wants to merge 1 commit into
Conversation
…MM blocking
RISC-V was the only major architecture without a get_L2_size() /
blas_set_parameter() implementation, so the GEMM cache-blocking parameters
(P/Q/R) were fixed at compile time regardless of the actual L2 cache size.
Because the blocking is now derived from the L2 cache detected at runtime
rather than a fixed compile-time constant, future RISC-V cores - which are
arriving with progressively larger and more varied L2 caches - get more
optimal blocking automatically, and the port gains the same runtime-tuning
hook x86 and LoongArch already use.
This adds, under ARCH_RISCV64:
- get_L2_size(): reads the level-2 (unified) cache size from Linux sysfs
(/sys/devices/system/cpu/cpu0/cache/index*/{level,size}); RISC-V has no
architectural cache-size query like x86 CPUID or LoongArch CPUCFG. Falls
back to 512 KB when sysfs is unavailable.
- blas_set_parameter(): scales each precision's packed-A dimension P from the
detected L2. The base blocking and the reference cache size come from the
active core's own param.h block (*_DEFAULT_P_BASE, RISCV_L2_REFERENCE_KB),
so the function carries no core-specific constants and is a no-op for cores
that do not opt in. Q and R keep their param.h defaults.
- driver/others/memory.c and common_macro.h: add ARCH_RISCV64 to the existing
architecture lists that call blas_set_parameter() and declare the runtime
parameter variables (sgemm_p, dgemm_p, ...).
- param.h RISCV64_ZVL256B: declares the per-core base blocking + reference and
maps SGEMM/DGEMM/CGEMM/ZGEMM DEFAULT_P to the runtime variables for static
builds; DYNAMIC_ARCH keeps the literals, since kernel/setparam-ref.c
init_parameter() initialises the gotoblas table from these macros and
blas_set_parameter() is not called on the dynamic path.
Only RISCV64_ZVL256B opts in so far; its base + reference are tuned on the
SpaceMiT X60, where a 512 KB L2 reproduces the stock blocking, so this is
performance-neutral on current hardware. Verified: a static RISCV64_ZVL256B
build reproduces the stock 128/128/16384 (SGEMM) and 64/128/8192 (DGEMM)
blocking; a DYNAMIC_ARCH build compiles cleanly (per-core setparam-ref objects
build without error); and get_L2_size() reads the correct size on both a
SpaceMiT X60 (512 KB L2) and a SiFive U74 / VisionFive 2 (2 MB L2).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
RISC-V: cache-aware GEMM blocking (get_L2_size + blas_set_parameter)
Summary
RISC-V is currently the only major architecture without a
get_L2_size()/blas_set_parameter()implementation, so the GEMM cache-blocking parameters (P/Q/R) are fixed at compile time regardless of the actual L2 cache. This PR adds cache-aware blocking for RISC-V, mirroring the existing x86 / LoongArch mechanism.Because the blocking is now derived from the L2 cache detected at runtime rather than a fixed compile-time constant, future RISC-V cores will get more optimal blocking automatically. The RISC-V hardware landscape is moving quickly — new cores are arriving with progressively larger and more varied L2 caches — and this lets a single library binary adapt its GEMM blocking to each part instead of shipping one hand-picked value per target (or, as today, one value for all). It also gives the RISC-V port the same runtime-tuning hook x86/LoongArch already use, so future per-core tuning has somewhere to live.
What it does
get_L2_size()(ARCH_RISCV64) — reads the level-2 (unified) cache size from Linux sysfs (/sys/devices/system/cpu/cpu0/cache/index*/{level,size}). RISC-V has no architectural cache-size query (x86 CPUID / LoongArch CPUCFG), so sysfs is the portable source; falls back to 512 KB when unavailable.blas_set_parameter()(ARCH_RISCV64) — scales the packed-A dimensionPfrom the detected L2, relative to a per-core reference cache size. The basePvalues and the reference (*_DEFAULT_P_BASE,RISCV_L2_REFERENCE_KB) live in each core'sparam.hblock, so the function carries no hard-coded, core-specific numbers and is a no-op for cores that don't declare a base;QandRkeep theirparam.hdefaults.ARCH_RISCV64into theblas_set_parameter()call sites (driver/others/memory.c) and the runtime-parameter extern declarations (common_macro.h).param.hRISCV64_ZVL256B: declares the per-core base blocking + reference and maps the*_DEFAULT_Pmacros to the runtime variables for static builds;DYNAMIC_ARCHkeeps the literals (see below).QandRare unchanged.DYNAMIC_ARCH
kernel/setparam-ref.c's RISC-Vinit_parameter()initialises thegotoblastable withTABLE_NAME.sgemm_p = SGEMM_DEFAULT_P, andblas_set_parameter()is not called on the dynamic path. So theparam.hchange is gated:#if defined(DYNAMIC_ARCH)keeps the integer literals (dynamic builds behave exactly as before),#elseuses the runtime variables (static builds get the cache-aware path).Calibration / scope
blas_set_parameter()carries no core-specific constants: it scales each core's own*_DEFAULT_P_BASEbyL2 / RISCV_L2_REFERENCE_KB, both declared in that core'sparam.hblock. OnlyRISCV64_ZVL256Bopts in so far, with a base + reference tuned on the SpaceMiT X60 — an empiricalP/Qsweep showed its stock 128/128 blocking is already optimal for the 512 KB shared L2, so at 512 KB this reproduces the stock blocking exactly (performance-neutral by construction). The payoff is forward-looking: a part with a larger L2 gets a proportionally larger panel automatically, and another core opts in simply by declaring its own base + reference — no change to the arch-wide function. Every other RISC-V core is untouched (theirparam.hliterals stand, andblas_set_parameter()is a no-op for them). Happy to adjust the scaling policy (e.g. discrete cache-size tiers like the LoongArch tables) if you'd prefer.Testing
RISCV64_ZVL256B, RVV), static build —get_L2_size()returns 512;blas_set_parameter()scales the ZVL256B base by 1 → effective blocking = stock (SGEMM 128/128/16384, DGEMM 64/128/8192); SGEMM throughput unchanged (no regression).DYNAMIC_ARCHbuild — theparam.hP macros resolve to the same literals as before (checked by preprocessing), sokernel/setparam-ref.c's table init is unchanged; a dynamic build compiles cleanly (per-coresetparam_RISCV64_*.oobjects build with no errors).rv64gcscalar, GCC 13.3) — the change compiles;get_L2_size()reads 2048 (the JH7110's 2 MB L2), confirming the sysfs detection generalizes. As a generality check, opting a second core in (the generic core) confirmedblas_set_parameter()scales that core's own base —dgemm_p128→512 from the generic base, not the X60's 64 — i.e. the function is genuinely per-core. That test also showed the plain total-L2 scale can overshoot a large shared cache (sa≈480 KiBvs the 512 KiB/core share → 4-core DGEMM 3.12 vs 3.43 GF), which is why only ZVL256B opts in and why the scaling policy is called out as adjustable above.Non-RISC-V builds are unaffected — every change is guarded by
ARCH_RISCV64.