summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2024-10-06 23:34:19 -0700
committerVito Caputo <vcaputo@pengaru.com>2024-10-08 01:31:31 -0700
commit8cd80cf9481f2a384a239b9fc1d02874aa7c64a7 (patch)
treeb840166e28867b615862bb7235fb8093fbb2b387
parent862c618c45d75d6f736f6b129943a5063e60c9eb (diff)
charts: try compute a real desired_delay_us
This introduces a concept of a "close enough" epsilon value. Where if the attempted update's current time is within very small temporal distance from the precisely scheduled time dictated by the interval, the update will still take a sample, rather than try introduce a tiny dely the host/kernel/ppoll will likely fail to adhere to without being tardy. Previously the desired delay was just a third of the interval, with no consideration for how long sampling took. This was dead simple, but made no attempt to schedule the poll timeout to align with the next sampling deadline, and would either cause excessive wakeups, or excessive tardiness, depending on the host's speed. I think this technically also fixed a bug where this_delta wouldn't get assigned if one of the earlier conditions short-circuited the later condition where it was being assigned.
-rw-r--r--src/charts.c54
1 files changed, 44 insertions, 10 deletions
diff --git a/src/charts.c b/src/charts.c
index fd66932..ae01c75 100644
--- a/src/charts.c
+++ b/src/charts.c
@@ -40,12 +40,13 @@
#include "vwm.h"
#endif
-#define CHART_ISTHREAD_ARGV "~" /* use this string to mark threads in the argv field */
-#define CHART_NOCOMM_ARGV "# missed it!" /* use this string to substitute the command when missing in argv field */
-#define CHART_MAX_ARGC 64 /* this is a huge amount */
-#define CHART_VMON_PROC_WANTS (VMON_WANT_PROC_STAT | VMON_WANT_PROC_FOLLOW_CHILDREN | VMON_WANT_PROC_FOLLOW_THREADS)
-#define CHART_VMON_SYS_WANTS (VMON_WANT_SYS_STAT)
-#define CHART_MAX_COLUMNS 16
+#define CHART_ISTHREAD_ARGV "~" /* use this string to mark threads in the argv field */
+#define CHART_NOCOMM_ARGV "# missed it!" /* use this string to substitute the command when missing in argv field */
+#define CHART_MAX_ARGC 64 /* this is a huge amount */
+#define CHART_VMON_PROC_WANTS (VMON_WANT_PROC_STAT | VMON_WANT_PROC_FOLLOW_CHILDREN | VMON_WANT_PROC_FOLLOW_THREADS)
+#define CHART_VMON_SYS_WANTS (VMON_WANT_SYS_STAT)
+#define CHART_MAX_COLUMNS 16
+#define CHART_DELTA_SECONDS_EPSILON .001f /* adherence errors smaller than this are treated as zero */
/* the global charts state, supplied to vwm_chart_create() which keeps a reference for future use. */
typedef struct _vwm_charts_t {
@@ -1296,16 +1297,29 @@ static float delta(struct timeval *cur, struct timeval *prev)
}
+static inline int delta_close_enough(vwm_charts_t *charts, float delta)
+{
+ float remainder = charts->sampling_interval_secs - delta;
+
+ /* if within .1ms of the scheduled next sample (or behind schedule at all), consider it "close enough" and take the sample. */
+ if (remainder <= CHART_DELTA_SECONDS_EPSILON)
+ return 1;
+
+ return 0;
+}
+
+
/* update the charts if necessary, return if updating occurred, and duration before another update needed in *desired_delay_us */
int vwm_charts_update(vwm_charts_t *charts, int *desired_delay_us)
{
+ int ret = 0, sampled = 0;
float this_delta = 0.0f;
- int ret = 0;
gettimeofday(&charts->maybe_sample, NULL);
+ this_delta = delta(&charts->maybe_sample, &charts->this_sample);
if (!charts->primed ||
(charts->sampling_interval_secs == INFINITY && !charts->sampling_paused) || /* XXX this is kind of a kludge to get the 0 Hz indicator drawn before pausing */
- (charts->sampling_interval_secs != INFINITY && ((this_delta = delta(&charts->maybe_sample, &charts->this_sample)) >= charts->sampling_interval_secs))) {
+ (charts->sampling_interval_secs != INFINITY && delta_close_enough(charts, this_delta))) {
vmon_sys_stat_t *sys_stat;
/* automatically lower the sample rate if we can't keep up with the current sample rate */
@@ -1359,10 +1373,30 @@ int vwm_charts_update(vwm_charts_t *charts, int *desired_delay_us)
/* "primed" is just a flag to ensure we always perform the first sample */
if (!charts->primed)
charts->primed = 1;
+
+ sampled = 1;
}
- /* TODO: make some effort to compute how long to sleep, but this is perfectly fine for now. */
- *desired_delay_us = charts->sampling_interval_secs == INFINITY ? -1 : charts->sampling_interval_secs * 300000.0;
+ if (charts->sampling_interval_secs == INFINITY) {
+ *desired_delay_us = -1; /* sleep forever */
+ } else {
+ float remaining_secs;
+
+ if (sampled) { /* sampling takes time, so let's subtract that from the interval-derived sleep time to try get the next sample started on-time (if possible) */
+ struct timeval post_sampled;
+
+ gettimeofday(&post_sampled, NULL);
+ this_delta += delta(&post_sampled, &charts->this_sample);
+ }
+
+ remaining_secs = charts->sampling_interval_secs - this_delta;
+ if (remaining_secs <= 0) {
+ /* always sleep some minimal amount, we don't want to spin */
+ *desired_delay_us = CHART_DELTA_SECONDS_EPSILON * 1000000.f;
+ } else {
+ *desired_delay_us = remaining_secs * 1000000.f;
+ }
+ }
return ret;
}
© All Rights Reserved