← Back to C-Kernel-Engine Docs Doxygen Source Documentation
 
Loading...
Searching...
No Matches
cpu_features.c File Reference
#include "cpu_features.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

Go to the source code of this file.

Functions

static void compute_gemm_params (const CPUInfo *cpu, GEMMParams *params)
 
void cpu_features_init (void)
 
static int detect_physical_cores (void)
 
const CPUInfoget_cpu_info (void)
 
const GEMMParamsget_gemm_params (void)
 
void print_cpu_info (void)
 

Variables

CPUInfo g_cpu_info = {0}
 
int g_cpu_initialized = 0
 
GEMMParams g_gemm_params = {0}
 

Function Documentation

◆ compute_gemm_params()

static void compute_gemm_params ( const CPUInfo cpu,
GEMMParams params 
)
static

Definition at line 300 of file cpu_features.c.

300 {
301 // Microkernel sizes based on SIMD width
302 // MUST match compile-time MR_FIXED/NR_FIXED in gemm_microkernel.c
303 if (cpu->has_avx512f) {
304 params->MR = 6; // 6 rows
305 params->NR = 32; // 32 cols (2 x ZMM registers)
306 } else if (cpu->has_fma) {
307 // AVX2+FMA: can use 6x16 with FMA hiding register spilling
308 params->MR = 6; // 6 rows
309 params->NR = 16; // 16 cols (2 x YMM registers)
310 } else if (cpu->has_avx || cpu->has_avx2) {
311 // AVX without FMA: use 4x16 to avoid register spilling
312 params->MR = 4; // 4 rows (reduced to fit in 16 YMM registers)
313 params->NR = 16; // 16 cols (2 x YMM registers)
314 } else {
315 params->MR = 4;
316 params->NR = 4;
317 }
318
319 // Get cache sizes (use defaults if detection failed)
320 size_t l1 = cpu->l1d_size > 0 ? cpu->l1d_size : 32 * 1024; // Default 32KB
321 size_t l2 = cpu->l2_size > 0 ? cpu->l2_size : 256 * 1024; // Default 256KB
322 size_t l3 = cpu->l3_size > 0 ? cpu->l3_size : 8 * 1024 * 1024; // Default 8MB
323
324 // BLIS-style blocking parameter computation
325 // Reference: "Anatomy of High-Performance Matrix Multiplication" (Goto & Van de Geijn)
326 //
327 // KC: Controls L1 usage
328 // - A micropanel: MR x KC
329 // - B micropanel: KC x NR (streamed from L2)
330 // - Want MR * KC * sizeof(float) to fit in ~half of L1
331 //
332 // MC: Controls L2 usage
333 // - A block: MC x KC should fit in L2
334 //
335 // NC: Controls L3 usage / main memory streaming
336 // - B panel: KC x NC
337
338 // KC: Controls L1 usage
339 // For optimal performance, both A micropanel and B row should fit in L1:
340 // - A micropanel: MR * KC floats
341 // - B row for streaming: NR floats per iteration (small)
342 // Use ~25% of L1 for A micropanel to leave room for B and working set
343 size_t l1_for_a = (l1 * 25) / 100;
344 params->KC = (int)(l1_for_a / (params->MR * sizeof(float)));
345
346 // Round KC to multiple of 8 for alignment
347 params->KC = (params->KC / 8) * 8;
348 if (params->KC < 64) params->KC = 64;
349 if (params->KC > 512) params->KC = 512; // Cap at 512 for better cache fit
350
351 // MC: A block = MC * KC * 4 bytes should fit in ~80% of L2
352 size_t l2_for_a = (l2 * 80) / 100;
353 params->MC = (int)(l2_for_a / (params->KC * sizeof(float)));
354
355 // Round MC to multiple of MR
356 params->MC = (params->MC / params->MR) * params->MR;
357 if (params->MC < params->MR * 4) params->MC = params->MR * 4;
358 if (params->MC > 512) params->MC = 512;
359
360 // NC: B panel = KC * NC * 4 bytes
361 // For L3, we want good streaming, use ~50% of L3 / num_cores
362 size_t l3_per_core = l3 / (cpu->num_cores > 0 ? cpu->num_cores : 1);
363 size_t l3_for_b = (l3_per_core * 50) / 100;
364 params->NC = (int)(l3_for_b / (params->KC * sizeof(float)));
365
366 // Round NC to multiple of NR
367 params->NC = (params->NC / params->NR) * params->NR;
368 if (params->NC < params->NR * 8) params->NC = params->NR * 8;
369 if (params->NC > 8192) params->NC = 8192;
370}
size_t l1d_size
size_t l3_size
int has_avx2
int has_fma
int has_avx
int has_avx512f
int num_cores
size_t l2_size

References CPUInfo::has_avx, CPUInfo::has_avx2, CPUInfo::has_avx512f, CPUInfo::has_fma, GEMMParams::KC, CPUInfo::l1d_size, CPUInfo::l2_size, CPUInfo::l3_size, GEMMParams::MC, GEMMParams::MR, GEMMParams::NC, GEMMParams::NR, and CPUInfo::num_cores.

Referenced by cpu_features_init().

◆ cpu_features_init()

void cpu_features_init ( void  )

Definition at line 376 of file cpu_features.c.

376 {
377 if (g_cpu_initialized) return;
378
379 memset(&g_cpu_info, 0, sizeof(g_cpu_info));
380 memset(&g_gemm_params, 0, sizeof(g_gemm_params));
381
382 // Detect SIMD features
383#ifdef X86_CPU
384 detect_x86_features(&g_cpu_info);
385 detect_x86_cache_sizes(&g_cpu_info);
386#endif
387
388 // Linux sysfs fallback for cache sizes
389#if defined(__linux__)
390 if (g_cpu_info.l1d_size == 0) {
391 detect_linux_cache_sizes(&g_cpu_info);
392 }
393#endif
394
395 // Detect physical cores
397
398 // Compute GEMM parameters based on detected hardware
400
402}
static void compute_gemm_params(const CPUInfo *cpu, GEMMParams *params)
CPUInfo g_cpu_info
static int detect_physical_cores(void)
int g_cpu_initialized
GEMMParams g_gemm_params

References compute_gemm_params(), detect_physical_cores(), g_cpu_info, g_cpu_initialized, g_gemm_params, CPUInfo::l1d_size, and CPUInfo::num_cores.

Referenced by get_cpu_info(), get_gemm_params(), and print_cpu_info().

◆ detect_physical_cores()

static int detect_physical_cores ( void  )
static

Definition at line 278 of file cpu_features.c.

278 {
279#if defined(__linux__)
280 return detect_linux_physical_cores();
281#elif defined(_WIN32)
282 SYSTEM_INFO sysinfo;
283 GetSystemInfo(&sysinfo);
284 // Windows: this gives logical processors, divide by 2 for HT estimate
285 return sysinfo.dwNumberOfProcessors / 2;
286#elif defined(__APPLE__)
287 int cores = 1;
288 size_t len = sizeof(cores);
289 sysctlbyname("hw.physicalcpu", &cores, &len, NULL, 0);
290 return cores;
291#else
292 return 1;
293#endif
294}

Referenced by cpu_features_init().

◆ get_cpu_info()

const CPUInfo * get_cpu_info ( void  )

Definition at line 409 of file cpu_features.c.

409 {
411 return &g_cpu_info;
412}
void cpu_features_init(void)

References cpu_features_init(), g_cpu_info, and g_cpu_initialized.

Referenced by gemm_init_threads().

◆ get_gemm_params()

const GEMMParams * get_gemm_params ( void  )

Definition at line 404 of file cpu_features.c.

404 {
406 return &g_gemm_params;
407}

References cpu_features_init(), g_cpu_initialized, and g_gemm_params.

◆ print_cpu_info()

void print_cpu_info ( void  )

Definition at line 414 of file cpu_features.c.

414 {
416
417 printf("=== CPU Info ===\n");
418 printf("Physical cores: %d\n", g_cpu_info.num_cores);
419 printf("L1 Data Cache: %zu KB\n", g_cpu_info.l1d_size / 1024);
420 printf("L2 Cache: %zu KB\n", g_cpu_info.l2_size / 1024);
421 printf("L3 Cache: %zu MB\n", g_cpu_info.l3_size / (1024 * 1024));
422 printf("Cache line: %zu bytes\n", g_cpu_info.l1_line_size);
423 printf("AVX: %s\n", g_cpu_info.has_avx ? "yes" : "no");
424 printf("AVX2: %s\n", g_cpu_info.has_avx2 ? "yes" : "no");
425 printf("AVX-512F: %s\n", g_cpu_info.has_avx512f ? "yes" : "no");
426 printf("AVX-512BW/DQ/VL:%s/%s/%s\n",
427 g_cpu_info.has_avx512bw ? "yes" : "no",
428 g_cpu_info.has_avx512dq ? "yes" : "no",
429 g_cpu_info.has_avx512vl ? "yes" : "no");
430 printf("AVX-512 VNNI: %s\n", g_cpu_info.has_avx512_vnni ? "yes" : "no");
431 printf("AVX-512 BF16: %s\n", g_cpu_info.has_avx512_bf16 ? "yes" : "no");
432 printf("AMX tile/int8/bf16: %s/%s/%s\n",
433 g_cpu_info.has_amx_tile ? "yes" : "no",
434 g_cpu_info.has_amx_int8 ? "yes" : "no",
435 g_cpu_info.has_amx_bf16 ? "yes" : "no");
436 printf("FMA: %s\n", g_cpu_info.has_fma ? "yes" : "no");
437 printf("\n=== GEMM Blocking Parameters ===\n");
438 printf("MR (microkernel rows): %d\n", g_gemm_params.MR);
439 printf("NR (microkernel cols): %d\n", g_gemm_params.NR);
440 printf("MC (M block): %d\n", g_gemm_params.MC);
441 printf("NC (N block): %d\n", g_gemm_params.NC);
442 printf("KC (K block): %d\n", g_gemm_params.KC);
443 printf("\n");
444}
int has_amx_tile
int has_avx512_bf16
int has_avx512bw
size_t l1_line_size
int has_avx512_vnni

References cpu_features_init(), g_cpu_info, g_cpu_initialized, g_gemm_params, CPUInfo::has_amx_bf16, CPUInfo::has_amx_int8, CPUInfo::has_amx_tile, CPUInfo::has_avx, CPUInfo::has_avx2, CPUInfo::has_avx512_bf16, CPUInfo::has_avx512_vnni, CPUInfo::has_avx512bw, CPUInfo::has_avx512dq, CPUInfo::has_avx512f, CPUInfo::has_avx512vl, CPUInfo::has_fma, GEMMParams::KC, CPUInfo::l1_line_size, CPUInfo::l1d_size, CPUInfo::l2_size, CPUInfo::l3_size, GEMMParams::MC, GEMMParams::MR, GEMMParams::NC, GEMMParams::NR, and CPUInfo::num_cores.

Variable Documentation

◆ g_cpu_info

CPUInfo g_cpu_info = {0}

CPU Feature Detection and Cache-Aware Parameter Tuning

Detects CPU features, cache sizes, and core counts at runtime. Computes optimal GEMM blocking parameters based on actual hardware.

Definition at line 28 of file cpu_features.c.

28{0};

Referenced by cpu_features_init(), get_cpu_info(), and print_cpu_info().

◆ g_cpu_initialized

int g_cpu_initialized = 0

Definition at line 30 of file cpu_features.c.

Referenced by cpu_features_init(), get_cpu_info(), get_gemm_params(), and print_cpu_info().

◆ g_gemm_params

GEMMParams g_gemm_params = {0}

Definition at line 29 of file cpu_features.c.

29{0};

Referenced by cpu_features_init(), get_gemm_params(), and print_cpu_info().