Skip to content

Commit 31c5780

Browse files
bushidocodesclaude
andauthored
fix: calibrate TSC frequency instead of scraping /proc/cpuinfo (#392)
The runtime times everything with __getcycles() (rdtsc), the invariant TSC, which ticks at a fixed rate independent of the core's current p-state. But runtime_get_processor_speed_MHz() derived its cycles<->us conversion factor from the instantaneous "cpu MHz" in /proc/cpuinfo, which reports the core's current operating frequency. That value varies under frequency scaling and TurboBoost (and exhibits natural per-read variance on recent Intel parts even under the performance governor), so the conversion factor was both wrong (it is not the TSC rate) and unstable across startups, corrupting deadline math. The previous workaround was hardcoding a value via SLEDGE_PROC_MHZ. Calibrate the TSC rate directly instead: count rdtsc cycles over timed CLOCK_MONOTONIC_RAW windows (5 x 20 ms) and take the maximum rate. A deschedule during a window only ever stretches the measured wall time, lowering the apparent rate, so the max rejects that noise. This measures the actual TSC frequency and is stable regardless of governor/p-state. The SLEDGE_PROC_MHZ override is preserved. Verified: calibrates 3793 MHz, stable across 6 restarts, matching an independent 1-second rdtsc measurement (3792.9 MHz); override and end-to-end request serving both work. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6037e62 commit 31c5780

1 file changed

Lines changed: 52 additions & 36 deletions

File tree

runtime/src/main.c

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <sys/stat.h>
99
#include <sys/time.h>
1010
#include <sys/types.h>
11+
#include <time.h>
1112
#include <unistd.h>
1213

1314
#ifdef LOG_TO_FILE
@@ -96,54 +97,69 @@ runtime_allocate_available_cores()
9697
pretty_print_key_value("Worker core count", "%u\n", runtime_worker_threads_count);
9798
}
9899

100+
static inline uint64_t
101+
runtime_monotonic_raw_ns(void)
102+
{
103+
struct timespec ts;
104+
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
105+
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
106+
}
107+
99108
/**
100-
* Returns the cpu MHz entry for CPU0 in /proc/cpuinfo, rounded to the nearest MHz
101-
* We are assuming all cores are the same clock speed, which is not true of many systems
102-
* We are also assuming this value is static
103-
* @return proceccor speed in MHz
109+
* Determines the frequency (in MHz) of the Time Stamp Counter that backs
110+
* __getcycles (rdtsc), which the runtime uses for all timekeeping and deadline
111+
* math. The TSC is "invariant" on modern x86-64 — it ticks at a fixed rate
112+
* regardless of the core's current p-state — so we calibrate it directly
113+
* against CLOCK_MONOTONIC_RAW. Scraping the instantaneous "cpu MHz" from
114+
* /proc/cpuinfo instead reports the core's current operating frequency, which
115+
* varies under frequency scaling and TurboBoost and does not equal the TSC rate.
116+
*
117+
* SLEDGE_PROC_MHZ may be set to override calibration with a fixed value.
104118
*/
105119
static inline void
106120
runtime_get_processor_speed_MHz(void)
107121
{
108122
char *proc_mhz_raw = getenv("SLEDGE_PROC_MHZ");
109-
FILE *cmd = NULL;
110123

111124
if (proc_mhz_raw != NULL) {
112-
/* The case with manual override for the CPU freq */
113-
runtime_processor_speed_MHz = atoi(proc_mhz_raw);
125+
/* Manual override */
126+
int override_MHz = atoi(proc_mhz_raw);
127+
if (unlikely(override_MHz <= 0)) panic("SLEDGE_PROC_MHZ must be a positive integer\n");
128+
runtime_processor_speed_MHz = (uint32_t)override_MHz;
114129
} else {
115-
/* The case when we have to get CPU freq from */
116-
usleep(200000); /* wait a bit for the workers to launch for more accuracy */
117-
118-
/* Get the average of the cpufreqs only for worker cores (no event core and reserved) */
119-
char command[128] = {0};
120-
sprintf(command, "grep '^cpu MHz' /proc/cpuinfo | sed -n '%u,%up' | \
121-
awk '{ total += $4; count++ } END { print total/count }'",
122-
runtime_first_worker_processor + 1,
123-
runtime_first_worker_processor + runtime_worker_threads_count);
124-
cmd = popen(command, "r");
125-
if (unlikely(cmd == NULL)) goto err;
126-
127-
char buff[16];
128-
size_t n = fread(buff, 1, sizeof(buff) - 1, cmd);
129-
buff[n] = '\0';
130-
pclose(cmd);
131-
132-
float processor_speed_MHz;
133-
n = sscanf(buff, "%f", &processor_speed_MHz);
134-
if (unlikely(n != 1)) goto err;
135-
if (unlikely(processor_speed_MHz < 0)) goto err;
136-
137-
runtime_processor_speed_MHz = (uint32_t)nearbyintf(processor_speed_MHz);
130+
/* Calibrate the TSC frequency against CLOCK_MONOTONIC_RAW.
131+
*
132+
* A deschedule during a window only ever stretches the measured wall time
133+
* (lowering the apparent rate), never the reverse, so taking the maximum
134+
* rate across several short windows rejects that noise. */
135+
const int iterations = 5;
136+
const struct timespec window = { .tv_sec = 0, .tv_nsec = 20 * 1000 * 1000 }; /* 20 ms */
137+
double best_MHz = 0.0;
138+
139+
for (int i = 0; i < iterations; i++) {
140+
uint64_t ns_start = runtime_monotonic_raw_ns();
141+
uint64_t cycles_start = __getcycles();
142+
143+
nanosleep(&window, NULL);
144+
145+
uint64_t cycles_end = __getcycles();
146+
uint64_t ns_end = runtime_monotonic_raw_ns();
147+
148+
uint64_t elapsed_ns = ns_end - ns_start;
149+
uint64_t elapsed_cycles = cycles_end - cycles_start;
150+
if (unlikely(elapsed_ns == 0)) continue;
151+
152+
/* MHz = cycles / microseconds = cycles * 1000 / nanoseconds */
153+
double mhz = (double)elapsed_cycles * 1000.0 / (double)elapsed_ns;
154+
if (mhz > best_MHz) best_MHz = mhz;
155+
}
156+
157+
if (unlikely(best_MHz <= 0.0)) panic("Failed to calibrate processor frequency\n");
158+
159+
runtime_processor_speed_MHz = (uint32_t)nearbyint(best_MHz);
138160
}
139161

140162
pretty_print_key_value("Worker CPU Freq", "%u MHz\n", runtime_processor_speed_MHz);
141-
142-
done:
143-
return;
144-
err:
145-
goto done;
146-
panic("Failed to detect processor frequency");
147163
}
148164

149165
/**

0 commit comments

Comments
 (0)