MEDIUM: clock: use the monotonic clock for idle time calculation

By just keeping a copy of the last known value before entering
polling, we can apply the same algorithm as we're currently using,
except that it's now applied to the monotonic clock instead of the
wall clock, when it's detected that it's ticking. This improves
idle time calculation accuracy by making it independent on the
wall clock.
This commit is contained in:
Willy Tarreau 2024-09-08 18:54:36 +00:00
parent 4150851ce5
commit 24496803d1

View File

@ -39,6 +39,7 @@ THREAD_LOCAL ullong now_ns; /* internal monotonic date der
THREAD_LOCAL uint now_ms; /* internal monotonic date in milliseconds (may wrap) */
THREAD_LOCAL struct timeval date; /* the real current date (wall-clock time) */
static THREAD_LOCAL ullong before_poll_mono_ns; /* system wide monotonic time when entering poll last */
static THREAD_LOCAL struct timeval before_poll; /* system date before calling poll() */
static THREAD_LOCAL struct timeval after_poll; /* system date after leaving poll() */
static THREAD_LOCAL unsigned int samp_time; /* total elapsed time over current sample */
@ -310,7 +311,8 @@ void clock_update_global_date()
void clock_init_process_date(void)
{
now_offset = 0;
th_ctx->prev_mono_time = th_ctx->curr_mono_time = now_mono_time(); // 0 if not supported
before_poll_mono_ns = now_mono_time(); // 0 if not supported
th_ctx->prev_mono_time = th_ctx->curr_mono_time = before_poll_mono_ns;
gettimeofday(&date, NULL);
after_poll = before_poll = date;
global_now_ns = th_ctx->curr_mono_time;
@ -356,6 +358,7 @@ void clock_init_thread_date(void)
th_ctx->prev_cpu_time = now_cpu_time();
th_ctx->prev_mono_time = now_mono_time();
th_ctx->curr_mono_time = th_ctx->prev_mono_time;
before_poll_mono_ns = th_ctx->curr_mono_time;
clock_update_date(0, 1);
}
@ -390,15 +393,23 @@ static inline void clock_measure_idle(void)
*/
int delta;
if ((delta = date.tv_sec - before_poll.tv_sec))
delta *= 1000000;
idle_time += delta + (date.tv_usec - before_poll.tv_usec);
if (before_poll_mono_ns) {
/* CLOCK_MONOTONIC in use, use it and convert it to microseconds */
if ((delta = date.tv_sec - after_poll.tv_sec))
delta *= 1000000;
samp_time += delta + (date.tv_usec - after_poll.tv_usec);
idle_time += (th_ctx->curr_mono_time - before_poll_mono_ns) / 1000ull;
samp_time += (th_ctx->curr_mono_time - th_ctx->prev_mono_time) / 1000ull;
} else {
/* CLOCK_MONOTONIC not used */
if ((delta = date.tv_sec - before_poll.tv_sec))
delta *= 1000000;
idle_time += delta + (date.tv_usec - before_poll.tv_usec);
after_poll.tv_sec = date.tv_sec; after_poll.tv_usec = date.tv_usec;
if ((delta = date.tv_sec - after_poll.tv_sec))
delta *= 1000000;
samp_time += delta + (date.tv_usec - after_poll.tv_usec);
after_poll.tv_sec = date.tv_sec; after_poll.tv_usec = date.tv_usec;
}
if (samp_time < 500000)
return;
@ -433,6 +444,12 @@ void clock_entering_poll(void)
gettimeofday(&before_poll, NULL);
new_cpu_time = now_cpu_time();
new_mono_time = now_mono_time();
/* the the time when we entere poll */
before_poll_mono_ns = new_mono_time;
/* The time might have jumped either backwards or forwards during tasks
* processing. It's easy to detect a backwards jump, but a forward jump
* needs a marging. Here the upper limit of 2 seconds corresponds to a
@ -449,10 +466,10 @@ void clock_entering_poll(void)
else if (unlikely(__tv_ms_elapsed(&after_poll, &before_poll) >= 2000))
tv_ms_add(&before_poll, &after_poll, 2000);
run_time = (before_poll.tv_sec - after_poll.tv_sec) * 1000000U + (before_poll.tv_usec - after_poll.tv_usec);
new_cpu_time = now_cpu_time();
new_mono_time = now_mono_time();
if (before_poll_mono_ns)
run_time = (before_poll_mono_ns - th_ctx->curr_mono_time) / 1000ull;
else
run_time = (before_poll.tv_sec - after_poll.tv_sec) * 1000000U + (before_poll.tv_usec - after_poll.tv_usec);
if (th_ctx->prev_cpu_time && th_ctx->prev_mono_time) {
new_cpu_time -= th_ctx->prev_cpu_time;