[v1.05] Experimental max frequency measurement (x86 only)

This commit is contained in:
Dr-Noob
2024-02-14 08:48:27 +00:00
parent cc16bc56ef
commit 72eee0e08e
6 changed files with 190 additions and 2 deletions

View File

@@ -7,8 +7,8 @@ PREFIX ?= /usr
SRC_COMMON=src/common/ SRC_COMMON=src/common/
COMMON_SRC = $(SRC_COMMON)main.c $(SRC_COMMON)cpu.c $(SRC_COMMON)udev.c $(SRC_COMMON)printer.c $(SRC_COMMON)args.c $(SRC_COMMON)global.c COMMON_SRC = $(SRC_COMMON)main.c $(SRC_COMMON)cpu.c $(SRC_COMMON)udev.c $(SRC_COMMON)printer.c $(SRC_COMMON)args.c $(SRC_COMMON)global.c $(SRC_COMMON)freq.c
COMMON_HDR = $(SRC_COMMON)ascii.h $(SRC_COMMON)cpu.h $(SRC_COMMON)udev.h $(SRC_COMMON)printer.h $(SRC_COMMON)args.h $(SRC_COMMON)global.h COMMON_HDR = $(SRC_COMMON)ascii.h $(SRC_COMMON)cpu.h $(SRC_COMMON)udev.h $(SRC_COMMON)printer.h $(SRC_COMMON)args.h $(SRC_COMMON)global.h $(SRC_COMMON)freq.h
ifneq ($(OS),Windows_NT) ifneq ($(OS),Windows_NT)
GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)" GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"

124
src/common/freq.c Normal file
View File

@@ -0,0 +1,124 @@
#ifdef __linux__
#define _GNU_SOURCE
// TODO: This should be under common/
#include "../x86/apic.h"
#include "../x86/freq/freq.h"
#include "global.h"
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
static long
perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags) {
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
return ret;
}
// Differences between x86 measure_frequency this measure_max_frequency:
// - measure_frequency employs all cores simultaneously wherease
// measure_max_frequency only employs 1.
// - measure_frequency runs the computation and checks /proc/cpuinfo whereas
// measure_max_frequency does not rely on /proc/cpuinfo and simply
// counts cpu cycles to measure frequency.
// - measure_frequency uses actual computation while measuring the frequency
// whereas measure_max_frequency uses nop instructions. This makes the former
// x86 dependant whereas the latter is architecture independant.
int64_t measure_max_frequency(void) {
// TODO: Support binding on all cores inside a SoC (x86
// hybrid topologies and ARM SoCs)
int core = 0;
if (!bind_to_cpu(core)) {
printErr("Failed binding the process to CPU %d", core);
return -1;
}
const char* frequency_banner = "cpufetch is measuring the max frequency...";
printf(frequency_banner);
fflush(stdout);
clockid_t clock = CLOCK_PROCESS_CPUTIME_ID;
struct perf_event_attr pe;
uint64_t instructions;
int fd;
int pid = 0;
void (*nop_function)(uint64_t);
#ifdef ARCH_X86
nop_function = nop_function_x86;
#elif ARCH_ARM
// TODO
nop_function = nop_function_arm;
#endif
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_CPU_CYCLES;
pe.disabled = 1;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = perf_event_open(&pe, pid, core, -1, 0);
if (fd == -1) {
perror("perf_event_open");
return -1;
}
uint64_t iters = 10000000;
struct timespec start, end;
if (clock_gettime(clock, &start) == -1) {
perror("clock_gettime");
return -1;
}
if(ioctl(fd, PERF_EVENT_IOC_RESET, 0) == -1) {
perror("ioctl");
return -1;
}
if(ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == -1) {
perror("ioctl");
return -1;
}
nop_function(iters);
read(fd, &instructions, sizeof(uint64_t));
if(ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == -1) {
perror("ioctl");
return -1;
}
if (clock_gettime(clock, &end) == -1) {
perror("clock_gettime");
return -1;
}
uint64_t nsecs = (end.tv_sec*1e9 + end.tv_nsec) - (start.tv_sec*1e9 + start.tv_nsec);
uint64_t usecs = nsecs/1000;
double frequency = instructions/((double)usecs);
printf("\r%*c\r", (int) strlen(frequency_banner), ' ');
printf("%ld %ld\n", instructions, usecs);
printf("%f\n", frequency);
// Discard last digit in the frequency which should help providing more reliable
// values.
return (((int) frequency + 5)/10) * 10;
}
#endif // #ifdef __linux__

6
src/common/freq.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef __COMMON_FREQ__
#define __COMMON_FREQ__
int64_t measure_max_frequency(void);
#endif

View File

@@ -6,6 +6,10 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef __linux__
#include "../common/freq.h"
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -960,6 +964,13 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
} }
} }
#ifdef __linux__
if (freq->max == UNKNOWN_DATA) {
printWarn("All previous methods failed, measuring CPU frequency");
freq->max = measure_max_frequency();
}
#endif
return freq; return freq;
} }

View File

@@ -144,3 +144,49 @@ int64_t measure_frequency(struct cpuInfo* cpu) {
printf("\r%*c", num_spaces, ' '); printf("\r%*c", num_spaces, ' ');
return freq_struct->freq; return freq_struct->freq;
} }
#define INSERT_ASM_ONCE __asm volatile("nop");
#define INSERT_ASM_10_TIMES \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
#define INSERT_ASM_100_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES
#define INSERT_ASM_1000_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
void nop_function_x86(uint64_t iters) {
for (uint64_t i = 0; i < iters; i++) {
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
}
}

View File

@@ -9,5 +9,6 @@
#define LOOP_ITERS 100000000 #define LOOP_ITERS 100000000
int64_t measure_frequency(struct cpuInfo* cpu); int64_t measure_frequency(struct cpuInfo* cpu);
void nop_function_x86(uint64_t iters);
#endif #endif