20#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
22#if defined(__GNUC__) || defined(__clang__)
37static void cpuid(
int leaf,
int subleaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) {
38#if defined(__GNUC__) || defined(__clang__)
39 __cpuid_count(leaf, subleaf, *eax, *ebx, *ecx, *edx);
40#elif defined(_MSC_VER)
42 __cpuidex(regs, leaf, subleaf);
43 *eax = regs[0]; *ebx = regs[1]; *ecx = regs[2]; *edx = regs[3];
45 *eax = *ebx = *ecx = *edx = 0;
49static uint64_t xgetbv0(
void) {
50#if defined(__GNUC__) || defined(__clang__)
51 uint32_t eax = 0, edx = 0;
52 __asm__ __volatile__(
".byte 0x0f, 0x01, 0xd0" :
"=a"(eax),
"=d"(edx) :
"c"(0));
53 return ((uint64_t)edx << 32) | eax;
54#elif defined(_MSC_VER)
61static void detect_x86_features(
CPUInfo* info) {
62 uint32_t eax, ebx, ecx, edx;
65 cpuid(0, 0, &eax, &ebx, &ecx, &edx);
66 uint32_t max_leaf = eax;
69 cpuid(1, 0, &eax, &ebx, &ecx, &edx);
70 int has_osxsave = (ecx >> 27) & 1;
71 uint64_t xcr0 = has_osxsave ? xgetbv0() : 0;
72 int os_avx = ((xcr0 & 0x6) == 0x6);
73 int os_avx512 = ((xcr0 & 0xE6) == 0xE6);
74 int os_amx = ((xcr0 & 0x60000) == 0x60000);
76 info->
has_avx = ((ecx >> 28) & 1) && os_avx;
77 info->
has_fma = (ecx >> 12) & 1;
80 cpuid(7, 0, &eax, &ebx, &ecx, &edx);
81 info->
has_avx2 = ((ebx >> 5) & 1) && os_avx;
94 cpuid(7, 1, &eax, &ebx, &ecx, &edx);
102static void detect_x86_cache_sizes(
CPUInfo* info) {
103 uint32_t eax, ebx, ecx, edx;
106 for (
int index = 0; index < 16; index++) {
107 cpuid(0x04, index, &eax, &ebx, &ecx, &edx);
109 int cache_type = eax & 0x1F;
110 if (cache_type == 0)
break;
112 int cache_level = (eax >> 5) & 0x7;
113 int line_size = (ebx & 0xFFF) + 1;
114 int partitions = ((ebx >> 12) & 0x3FF) + 1;
115 int ways = ((ebx >> 22) & 0x3FF) + 1;
118 size_t cache_size = (size_t)line_size * partitions * ways * sets;
120 if (cache_type == 1 || cache_type == 3) {
121 if (cache_level == 1) {
124 }
else if (cache_level == 2) {
126 }
else if (cache_level == 3) {
134 cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx);
135 if (eax >= 0x8000001D) {
136 for (
int index = 0; index < 16; index++) {
137 cpuid(0x8000001D, index, &eax, &ebx, &ecx, &edx);
139 int cache_type = eax & 0x1F;
140 if (cache_type == 0)
break;
142 int cache_level = (eax >> 5) & 0x7;
143 int line_size = (ebx & 0xFFF) + 1;
144 int partitions = ((ebx >> 12) & 0x3FF) + 1;
145 int ways = ((ebx >> 22) & 0x3FF) + 1;
148 size_t cache_size = (size_t)line_size * partitions * ways * sets;
150 if (cache_type == 1 || cache_type == 3) {
151 if (cache_level == 1) {
154 }
else if (cache_level == 2) {
156 }
else if (cache_level == 3) {
170#if defined(__linux__)
171static size_t read_sysfs_cache_size(
int cpu,
int index) {
173 snprintf(path,
sizeof(path),
174 "/sys/devices/system/cpu/cpu%d/cache/index%d/size", cpu, index);
176 FILE* f = fopen(path,
"r");
180 if (!fgets(buf,
sizeof(buf), f)) {
188 sscanf(buf,
"%zu%c", &size, &unit);
190 if (unit ==
'K' || unit ==
'k') size *= 1024;
191 else if (unit ==
'M' || unit ==
'm') size *= 1024 * 1024;
196static int read_sysfs_cache_level(
int cpu,
int index) {
198 snprintf(path,
sizeof(path),
199 "/sys/devices/system/cpu/cpu%d/cache/index%d/level", cpu, index);
201 FILE* f = fopen(path,
"r");
205 if (fscanf(f,
"%d", &level) != 1) level = -1;
210static void detect_linux_cache_sizes(
CPUInfo* info) {
212 for (
int index = 0; index < 10; index++) {
213 size_t size = read_sysfs_cache_size(0, index);
214 if (size == 0)
break;
216 int level = read_sysfs_cache_level(0, index);
220 snprintf(path,
sizeof(path),
221 "/sys/devices/system/cpu/cpu0/cache/index%d/type", index);
222 FILE* f = fopen(path,
"r");
225 if (fscanf(f,
"%31s", type) != 1) type[0] =
'\0';
228 if (strcmp(type,
"Data") == 0 || strcmp(type,
"Unified") == 0) {
229 if (level == 1) info->
l1d_size = size;
230 else if (level == 2) info->
l2_size = size;
231 else if (level == 3) info->
l3_size = size;
237static int detect_linux_physical_cores(
void) {
239 int max_core_id = -1;
240 int num_processors = 0;
242 FILE* f = fopen(
"/proc/cpuinfo",
"r");
246 while (fgets(line,
sizeof(line), f)) {
247 if (strncmp(line,
"processor", 9) == 0) {
250 if (strncmp(line,
"cpu cores", 9) == 0) {
252 sscanf(line,
"cpu cores : %d", &cores);
258 if (strncmp(line,
"core id", 7) == 0) {
260 sscanf(line,
"core id : %d", &core_id);
261 if (core_id > max_core_id) max_core_id = core_id;
267 if (max_core_id >= 0) {
268 return max_core_id + 1;
270 return num_processors > 0 ? num_processors : 1;
279#if defined(__linux__)
280 return detect_linux_physical_cores();
283 GetSystemInfo(&sysinfo);
285 return sysinfo.dwNumberOfProcessors / 2;
286#elif defined(__APPLE__)
288 size_t len =
sizeof(cores);
289 sysctlbyname(
"hw.physicalcpu", &cores, &len, NULL, 0);
343 size_t l1_for_a = (l1 * 25) / 100;
344 params->
KC = (int)(l1_for_a / (params->
MR *
sizeof(
float)));
347 params->
KC = (params->
KC / 8) * 8;
348 if (params->
KC < 64) params->
KC = 64;
349 if (params->
KC > 512) params->
KC = 512;
352 size_t l2_for_a = (l2 * 80) / 100;
353 params->
MC = (int)(l2_for_a / (params->
KC *
sizeof(
float)));
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;
363 size_t l3_for_b = (l3_per_core * 50) / 100;
364 params->
NC = (int)(l3_for_b / (params->
KC *
sizeof(
float)));
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;
389#if defined(__linux__)
417 printf(
"=== CPU Info ===\n");
426 printf(
"AVX-512BW/DQ/VL:%s/%s/%s\n",
432 printf(
"AMX tile/int8/bf16: %s/%s/%s\n",
437 printf(
"\n=== GEMM Blocking Parameters ===\n");
void print_cpu_info(void)
static void compute_gemm_params(const CPUInfo *cpu, GEMMParams *params)
const GEMMParams * get_gemm_params(void)
const CPUInfo * get_cpu_info(void)
static int detect_physical_cores(void)
void cpu_features_init(void)