Merge pull request #5909 from hmeiland/riscv64-blas-set-parameter

RISC-V: cache-aware GEMM blocking (get_L2_size + blas_set_parameter)
This commit is contained in:
Martin Kroeker
2026-07-14 12:27:10 +02:00
committed by GitHub
4 changed files with 74 additions and 7 deletions
+1 -1
View File
@@ -2712,7 +2712,7 @@
#ifndef ASSEMBLER
#if !defined(DYNAMIC_ARCH) \
&& (defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64) \
|| defined(ARCH_LOONGARCH64) || defined(ARCH_E2K) || defined(ARCH_ALPHA))
|| defined(ARCH_LOONGARCH64) || defined(ARCH_E2K) || defined(ARCH_ALPHA) || defined(ARCH_RISCV64))
extern BLASLONG gemm_offset_a;
extern BLASLONG gemm_offset_b;
extern BLASLONG bgemm_p;
+2 -2
View File
@@ -1220,7 +1220,7 @@ UNLOCK_COMMAND(&alloc_lock);
if (!blas_num_threads) blas_cpu_number = blas_get_cpu_number();
#endif
#if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64)
#if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64) || defined(ARCH_RISCV64)
#ifndef DYNAMIC_ARCH
blas_set_parameter();
#endif
@@ -2822,7 +2822,7 @@ void *blas_memory_alloc(int procpos){
if (!blas_num_threads) blas_cpu_number = blas_get_cpu_number();
#endif
#if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64)
#if defined(ARCH_X86) || defined(ARCH_X86_64) || defined(ARCH_IA64) || defined(ARCH_MIPS64) || defined(ARCH_ARM64) || defined(ARCH_LOONGARCH64) || defined(ARCH_RISCV64)
#ifndef DYNAMIC_ARCH
blas_set_parameter();
#endif
+50
View File
@@ -902,3 +902,53 @@ void blas_set_parameter(void)
}
#endif
#if defined(ARCH_RISCV64)
#include <stdio.h>
/* RISC-V has no architectural cache-size query (cf. x86 CPUID / LoongArch
CPUCFG), so read the L2 (level 2, unified) size from Linux sysfs and fall
back to 512 KB. Returns the L2 size in kilobytes. */
int get_L2_size(void) {
int size = 0;
#if defined(OS_LINUX) || defined(OS_ANDROID)
int idx;
for (idx = 0; idx <= 4; idx++) {
char path[80]; FILE *fp; int level = 0; long val = 0; char unit = 0;
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/level", idx);
fp = fopen(path, "r"); if (fp == NULL) continue;
if (fscanf(fp, "%d", &level) != 1) { fclose(fp); continue; }
fclose(fp); if (level != 2) continue;
snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu0/cache/index%d/size", idx);
fp = fopen(path, "r"); if (fp == NULL) continue;
if (fscanf(fp, "%ld%c", &val, &unit) >= 1) {
if (unit == 'M' || unit == 'm') val *= 1024;
if (unit == 'G' || unit == 'g') val *= 1024 * 1024;
size = (int)val;
}
fclose(fp); if (size > 0) break;
}
#endif
if (size <= 0) size = 512;
return size;
}
void blas_set_parameter(void) {
#if defined(SGEMM_DEFAULT_P_BASE)
/* Scale each precision's packed-A dimension P from the detected L2, relative
to the size the active core's base blocking targets (RISCV_L2_REFERENCE_KB).
The bases come from the core's own param.h block, so this is not tied to any
single core; Q and R keep their param.h defaults. */
int l2 = get_L2_size(); /* KB */
int scale = l2 / RISCV_L2_REFERENCE_KB;
if (scale < 1) scale = 1;
if (scale > 4) scale = 4;
sgemm_p = SGEMM_DEFAULT_P_BASE * scale;
dgemm_p = DGEMM_DEFAULT_P_BASE * scale;
cgemm_p = CGEMM_DEFAULT_P_BASE * scale;
zgemm_p = ZGEMM_DEFAULT_P_BASE * scale;
#endif
}
#endif
+21 -4
View File
@@ -3304,10 +3304,27 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define SHGEMM_DEFAULT_P 128
#undef SBGEMM_DEFAULT_P
#define SBGEMM_DEFAULT_P 128
#define SGEMM_DEFAULT_P 128
#define DGEMM_DEFAULT_P 64
#define CGEMM_DEFAULT_P 64
#define ZGEMM_DEFAULT_P 64
/* Base packed-A (P) blocking for this core. On static builds blas_set_parameter()
scales P from the L2 cache detected at runtime, relative to RISCV_L2_REFERENCE_KB
(the L2 size these bases target); Q and R keep their param.h defaults. A cache
equal to the reference reproduces the stock blocking. DYNAMIC_ARCH uses the
literals directly (kernel/setparam-ref.c fills the gotoblas table from them). */
#define RISCV_L2_REFERENCE_KB 512
#define SGEMM_DEFAULT_P_BASE 128
#define DGEMM_DEFAULT_P_BASE 64
#define CGEMM_DEFAULT_P_BASE 64
#define ZGEMM_DEFAULT_P_BASE 64
#if defined(DYNAMIC_ARCH)
#define SGEMM_DEFAULT_P SGEMM_DEFAULT_P_BASE
#define DGEMM_DEFAULT_P DGEMM_DEFAULT_P_BASE
#define CGEMM_DEFAULT_P CGEMM_DEFAULT_P_BASE
#define ZGEMM_DEFAULT_P ZGEMM_DEFAULT_P_BASE
#else
#define SGEMM_DEFAULT_P sgemm_p
#define DGEMM_DEFAULT_P dgemm_p
#define CGEMM_DEFAULT_P cgemm_p
#define ZGEMM_DEFAULT_P zgemm_p
#endif
#undef SHGEMM_DEFAULT_Q
#define SHGEMM_DEFAULT_Q 128