From e1d3adb3d472f093de6436c44c6af52f1522b2cb Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Sat, 6 Jun 2026 17:50:15 +0200 Subject: [PATCH] In the speed test, set the CPU affinity to the expected best core --- cmdline/portable.h | 5 ++ cmdline/speed.c | 26 ++++++- cmdline/unix.c | 178 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) diff --git a/cmdline/portable.h b/cmdline/portable.h index f4f17266..8dacbbd1 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -400,6 +400,11 @@ char* os_fgets(char* s, int size, OS_FILE* stream); */ int os_pclose(OS_FILE* stream); +/** + * Get the optimal CPU for the speed test + */ +int os_get_optimal_cpu(void); + /****************************************************************************/ /* app */ diff --git a/cmdline/speed.c b/cmdline/speed.c index ff36d309..9653f084 100644 --- a/cmdline/speed.c +++ b/cmdline/speed.c @@ -112,10 +112,10 @@ void speed_crc(int nd, void** v, int size, int delta, int period) } SPEED_STOP printf("%8" PRIu64, ds / dt); + printf("\n"); } #endif printf("\n"); - printf("\n"); } void speed_hash(int nd, void** v, int size, int delta, int period) @@ -1399,6 +1399,29 @@ void speed_rec(int nd, void** v, int size, int delta, int period) printf("\n"); } +void speed_affinity(void) +{ +#if HAVE_LINUX_DEVICE + int num_cpus = sysconf(_SC_NPROCESSORS_CONF); + if (num_cpus <= 0) + return; + + printf("Machine has %d cores\n", num_cpus); + + int cpu = os_get_optimal_cpu(); + if (cpu <= 0) + return; + + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + + if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == 0) { + printf("Running on core %d\n", cpu); + } +#endif +} + void speed(int period, int nd, int size) { int i; @@ -1480,6 +1503,7 @@ void speed(int period, int nd, int size) #else printf("CPU of unknown architecture\n"); #endif + speed_affinity(); #if WORDS_BIGENDIAN printf("Memory is big-endian %d-bit\n", (int)sizeof(void*) * 8); #else diff --git a/cmdline/unix.c b/cmdline/unix.c index 5708da4b..7616b814 100644 --- a/cmdline/unix.c +++ b/cmdline/unix.c @@ -1167,6 +1167,184 @@ static int sysattr(const char* path, char* buf, size_t buf_size) } #endif +/******************************************************************************/ +/* cpu */ + +#if HAVE_LINUX_DEVICE +static long long syslong(const char* path) +{ + char buf[32]; + if (sysattr(path, buf, sizeof(buf)) != 0) + return -1; + char* end; + long long v = strtoll(buf, &end, 10); + if (end == buf) + return -1; + + return v; +} + +static unsigned long long syshex(const char* path) +{ + char buf[1152]; /* enough for 1024 CPUs */ + if (sysattr(path, buf, sizeof(buf)) != 0) + return 0; + + unsigned long long v = 0; + for (char* p = buf; *p != 0 && *p != '\n'; ++p) { + if (*p == ',') + continue; + + v <<= 4; + if (*p >= '0' && *p <= '9') + v |= *p - '0'; + else if (*p >= 'a' && *p <= 'f') + v |= *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + v |= *p - 'A' + 10; + else + break; /* hit unexpected character */ + } + + return v; +} + +static int cpu_is_online(int i) +{ + char path[256]; + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/online", i); + long long v = syslong(path); + if (v < 0) + return 1; /* cpu0 has no "online" knob — always on */ + return v; +} + +/* + * Returns 1 if the specified cpu is the lowest-numbered logical CPU in its + * physical core's SMT sibling set. + * + * thread_siblings is a hex bitmask where bit N = logical cpu N. + * The lowest set bit identifies the primary sibling. + * Safe for up to 64 logical CPUs (fits in unsigned long). + */ +static int cpu_is_primary_sibling(int i) +{ + char path[256]; + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i); + unsigned long long mask = syshex(path); + if (mask == 0) /* assume primary */ + return 1; + return __builtin_ctzll(mask) == i; +} + +#define CPUS_MAX 64 + +/** + * os_get_optimal_cpu() – returns the logical CPU index of the "fastest" + * core using a three-tier strategy: + * + * Tier 1 – ACPI CPPC preferred-core ranking (hardware-binned quality) + * Tier 2 – cpufreq max-frequency (P-core vs E-core discrimination) + * Tier 3 – SMT topology isolation (primary sibling only) + */ +int os_get_optimal_cpu(void) +{ + char path[256]; + + int num_cpus = sysconf(_SC_NPROCESSORS_CONF); + if (num_cpus <= 0) + return -1; + if (num_cpus > CPUS_MAX) + num_cpus = CPUS_MAX; + + /* ACPI CPPC: highest_perf with nominal_perf tiebreaker */ + long long max_highest = -1; + long long max_nominal = -1; + int best_cppc = -1; + for (int i = 0; i < num_cpus; ++i) { + if (!cpu_is_online(i)) + continue; + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/acpi_cppc/highest_perf", i); + long long hp = syslong(path); + if (hp < 0) + continue; + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/acpi_cppc/nominal_perf", i); + long long np = syslong(path); + /* np may be missing */ + + /* tiebreaker priority: highest_perf > nominal_perf > non-CPU0 */ + int better = (hp > max_highest) + || (hp == max_highest && np > max_nominal) + || (hp == max_highest && np == max_nominal && best_cppc == 0); + if (better) { + max_highest = hp; + max_nominal = np; + best_cppc = i; + } + } + + if (best_cppc >= 0) + return best_cppc; + + /* max frequency, then SMT isolation */ + + /* find the global maximum frequency among online CPUs */ + long long global_max_freq = -1; + for (int i = 0; i < num_cpus; ++i) { + if (!cpu_is_online(i)) + continue; + + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i); + long long freq = syslong(path); + if (freq < 0) + continue; + + if (freq > global_max_freq) + global_max_freq = freq; + } + + /* first online CPU at max freq that is a primary sibling, preferring non-CPU0 */ + int best_isolated = -1; /* primary sibling, not CPU 0 */ + int best_any = -1; /* any matching CPU, not CPU 0 */ + int best_c0 = -1; /* CPU 0 fallback (always a primary sibling) */ + + for (int i = 0; i < num_cpus; ++i) { + if (!cpu_is_online(i)) + continue; + + if (global_max_freq != -1) { + snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i); + if (syslong(path) != global_max_freq) + continue; + } + + if (i == 0) { + best_c0 = 0; + } else { + if (best_isolated == -1 && cpu_is_primary_sibling(i)) + best_isolated = i; + if (best_any == -1) + best_any = i; + } + } + + /* prefer non-CPU0, fall back to CPU0 only if no alternative exists */ + if (best_isolated != -1) + return best_isolated; + if (best_any != -1) + return best_any; + if (best_c0 != -1) + return best_c0; + + return -1; +} +#endif + +/******************************************************************************/ +/* dev */ + struct dev_struct { uint64_t device; /**< Device ID. */ tommy_node node;