@cryptotaxi247 / netdata-1 / commits / a0fd32537

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 }