perf(ml): reuse onewayalloc arena across hosts in detection loop (#22250)
* Refactor ml_host_detect_once and ml_update_host_and_detection_rate_charts to reuse ONEWAYALLOC arena for improved memory management * Improve comments on onewayalloc arena usage for memory management in detection thread * Rename parameter 'AnomalyRate' to 'anomaly_rate' for consistency in ml_update_host_and_detection_rate_charts
Stelios Fragkakis committed
Apr 23, 2026 at 17:24 UTC
a0fd325370fcd58ab01c0583b76d1a4d91569f1f
3 files changed
+22
-11
src/ml/ad_charts.cc
+8
-7
@@ -250,7 +250,7 @@ void ml_update_dimensions_chart(ml_host_t *host, const ml_machine_learning_stats
250
}
251
}
252
253
-void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number AnomalyRate) {
253
+void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number anomaly_rate, ONEWAYALLOC *owa) {
254
/*
255
* Host anomaly rate
256
*/
@@ -283,7 +283,7 @@ void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number
283
rrddim_add(host->anomaly_rate_rs, "anomaly_rate", NULL, 1, 100, RRD_ALGORITHM_ABSOLUTE);
284
}
285
286
- rrddim_set_by_pointer(host->anomaly_rate_rs, host->anomaly_rate_rd, AnomalyRate);
286
+ rrddim_set_by_pointer(host->anomaly_rate_rs, host->anomaly_rate_rd, anomaly_rate);
287
288
rrdset_done(host->anomaly_rate_rs);
289
}
@@ -378,14 +378,17 @@ void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number
378
* Compute the values of the dimensions based on the host rate chart
379
*/
380
if (host->ml_running) {
381
- ONEWAYALLOC *OWA = onewayalloc_create(0);
381
+ // Reclaim the previous host's query scratch before starting the
382
+ // next one. Cheap no-op on the first iteration of a fresh arena.
383
+ onewayalloc_reset(owa);
384
+
385
time_t Now = now_realtime_sec();
386
time_t Before = Now - host->rh->rrd_update_every;
387
time_t After = Before - Cfg.anomaly_detection_query_duration;
388
RRDR_OPTIONS Options = static_cast<RRDR_OPTIONS>(0x00000000);
389
390
RRDR *R = rrd2rrdr_legacy(
388
- OWA,
391
+ owa,
392
host->anomaly_rate_rs,
393
1 /* points wanted */,
394
After,
@@ -415,10 +418,8 @@ void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number
418
rrdset_done(host->detector_events_rs);
419
}
420
418
- rrdr_free(OWA, R);
421
+ rrdr_free(owa, R);
422
}
420
-
421
- onewayalloc_destroy(OWA);
423
} else {
424
rrddim_set_by_pointer(host->detector_events_rs,
425
host->detector_events_above_threshold_rd, 0);
src/ml/ad_charts.h
+1
-1
@@ -7,7 +7,7 @@
7
8
void ml_update_dimensions_chart(ml_host_t *host, const ml_machine_learning_stats_t &mls);
9
10
-void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number anomaly_rate);
10
+void ml_update_host_and_detection_rate_charts(ml_host_t *host, collected_number anomaly_rate, ONEWAYALLOC *owa);
11
12
void ml_update_training_statistics_chart(ml_worker_t *worker, const ml_queue_stats_t &ts);
13
src/ml/ml.cc
+13
-3
@@ -1006,7 +1006,7 @@ ml_chart_update_dimension(ml_chart_t *chart, ml_dimension_t *dim, bool is_anomal
1006
#define WORKER_JOB_DETECTION_STATS 3
1007
1008
static void
1009
-ml_host_detect_once(ml_host_t *host)
1009
+ml_host_detect_once(ml_host_t *host, ONEWAYALLOC *owa)
1010
{
1011
worker_is_busy(WORKER_JOB_DETECTION_COLLECT_STATS);
1012
@@ -1081,7 +1081,7 @@ ml_host_detect_once(ml_host_t *host)
1081
ml_update_dimensions_chart(host, mls_copy);
1082
1083
worker_is_busy(WORKER_JOB_DETECTION_HOST_CHART);
1084
- ml_update_host_and_detection_rate_charts(host, host->host_anomaly_rate * 10000.0);
1084
+ ml_update_host_and_detection_rate_charts(host, host->host_anomaly_rate * 10000.0, owa);
1085
} else {
1086
host->host_anomaly_rate = 0.0;
1087
@@ -1109,6 +1109,13 @@ void ml_detect_main(void *arg)
1109
heartbeat_t hb;
1110
heartbeat_init(&hb, USEC_PER_SEC);
1111
1112
+ // Single onewayalloc arena reused across every host and loop iteration
1113
+ // for the whole detect thread lifetime — one mmap/munmap pair instead
1114
+ // of one per host per second. The arena is reset between hosts inside
1115
+ // ml_update_host_and_detection_rate_charts, so peak memory stays bounded
1116
+ // by a single host's anomaly-rate query scratch.
1117
+ ONEWAYALLOC *detect_owa = onewayalloc_create(0);
1118
+
1119
while (!Cfg.detection_stop && service_running(SERVICE_COLLECTORS)) {
1120
worker_is_idle();
1121
heartbeat_next(&hb);
@@ -1122,7 +1129,7 @@ void ml_detect_main(void *arg)
1129
if (!service_running(SERVICE_COLLECTORS))
1130
break;
1131
1125
- ml_host_detect_once((ml_host_t *) rh->ml_host);
1132
+ ml_host_detect_once((ml_host_t *) rh->ml_host, detect_owa);
1133
}
1134
rrd_rdunlock();
1135
@@ -1139,6 +1146,9 @@ void ml_detect_main(void *arg)
1146
}
1147
}
1148
}
1149
+
1150
+ onewayalloc_destroy(detect_owa);
1151
+
1152
Cfg.training_stop = true;
1153
finalize_self_prepared_sql_statements();
1154
}