@cryptotaxi247 / netdata-1 / commits / 77a304f52

improve performance of worker utilization statistics (#14034)

Costa Tsaousis committed Nov 22, 2022 at 22:41 UTC 77a304f52e4c6aadef0eac06b4869b7e1c829175
4 files changed +88 -51
daemon/global_statistics.c
+43 -35
@@ -1697,9 +1697,9 @@ struct worker_job_type_gs {
1697
1698 struct worker_thread {
1699 pid_t pid;
1700 - int enabled;
1700 + bool enabled;
1701
1702 - int cpu_enabled;
1702 + bool cpu_enabled;
1703 double cpu;
1704
1705 kernel_uint_t utime;
@@ -1715,6 +1715,7 @@ struct worker_thread {
1715 usec_t busy_time;
1716
1717 struct worker_thread *next;
1718 + struct worker_thread *prev;
1719 };
1720
1721 struct worker_utilization {
@@ -1727,6 +1728,7 @@ struct worker_utilization {
1728
1729 struct worker_job_type_gs per_job_type[WORKER_UTILIZATION_MAX_JOB_TYPES];
1730
1731 + size_t workers_max_job_id;
1732 size_t workers_registered;
1733 size_t workers_busy;
1734 usec_t workers_total_busy_time;
@@ -1975,7 +1977,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
1977
1978 {
1979 size_t i;
1978 - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
1980 + for(i = 0; i <= wu->workers_max_job_id ;i++) {
1981 if(unlikely(wu->per_job_type[i].type != WORKER_METRIC_IDLE_BUSY))
1982 continue;
1983
@@ -2018,7 +2020,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
2020
2021 {
2022 size_t i;
2021 - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
2023 + for(i = 0; i <= wu->workers_max_job_id ;i++) {
2024 if(unlikely(wu->per_job_type[i].type != WORKER_METRIC_IDLE_BUSY))
2025 continue;
2026
@@ -2073,7 +2075,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
2075
2076 {
2077 size_t i;
2076 - for (i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES; i++) {
2078 + for (i = 0; i <= wu->workers_max_job_id; i++) {
2079 if(wu->per_job_type[i].type != WORKER_METRIC_ABSOLUTE)
2080 continue;
2081
@@ -2129,7 +2131,7 @@ static void workers_utilization_update_chart(struct worker_utilization *wu) {
2131
2132 {
2133 size_t i;
2132 - for (i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES; i++) {
2134 + for (i = 0; i <= wu->workers_max_job_id ; i++) {
2135 if(wu->per_job_type[i].type != WORKER_METRIC_INCREMENT && wu->per_job_type[i].type != WORKER_METRIC_INCREMENTAL_TOTAL)
2136 continue;
2137
@@ -2214,8 +2216,8 @@ static void workers_utilization_reset_statistics(struct worker_utilization *wu)
2216
2217 struct worker_thread *wt;
2218 for(wt = wu->threads; wt ; wt = wt->next) {
2217 - wt->enabled = 0;
2218 - wt->cpu_enabled = 0;
2219 + wt->enabled = false;
2220 + wt->cpu_enabled = false;
2221 }
2222 }
2223
@@ -2243,31 +2245,30 @@ static int read_thread_cpu_time_from_proc_stat(pid_t pid __maybe_unused, kernel_
2245 #endif
2246 }
2247
2248 +static Pvoid_t workers_by_pid_JudyL_array = NULL;
2249 +
2250 static void workers_threads_cleanup(struct worker_utilization *wu) {
2247 - struct worker_thread *t;
2248 -
2249 - // free threads at the beginning of the linked list
2250 - while(wu->threads && !wu->threads->enabled) {
2251 - t = wu->threads;
2252 - wu->threads = t->next;
2253 - t->next = NULL;
2254 - freez(t);
2251 + struct worker_thread *t = wu->threads;
2252 + while(t) {
2253 + struct worker_thread *next = t->next;
2254 +
2255 + if(!t->enabled) {
2256 + JudyLDel(&workers_by_pid_JudyL_array, t->pid, PJE0);
2257 + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(wu->threads, t, prev, next);
2258 + freez(t);
2259 + }
2260 +
2261 + t = next;
2262 }
2263 + }
2264
2257 - // free threads in the middle of the linked list
2258 - for(t = wu->threads; t && t->next ; t = t->next) {
2259 - if(t->next->enabled) continue;
2265 +static struct worker_thread *worker_thread_find(struct worker_utilization *wu __maybe_unused, pid_t pid) {
2266 + struct worker_thread *wt = NULL;
2267
2261 - struct worker_thread *to_remove = t->next;
2262 - t->next = to_remove->next;
2263 - to_remove->next = NULL;
2264 - freez(to_remove);
2265 - }
2266 -}
2268 + Pvoid_t *PValue = JudyLGet(workers_by_pid_JudyL_array, pid, PJE0);
2269 + if(PValue)
2270 + wt = *PValue;
2271
2268 -static struct worker_thread *worker_thread_find(struct worker_utilization *wu, pid_t pid) {
2269 - struct worker_thread *wt;
2270 - for(wt = wu->threads; wt && wt->pid != pid ; wt = wt->next) ;
2272 return wt;
2273 }
2274
@@ -2277,9 +2278,11 @@ static struct worker_thread *worker_thread_create(struct worker_utilization *wu,
2278 wt = (struct worker_thread *)callocz(1, sizeof(struct worker_thread));
2279 wt->pid = pid;
2280
2281 + Pvoid_t *PValue = JudyLIns(&workers_by_pid_JudyL_array, pid, PJE0);
2282 + *PValue = wt;
2283 +
2284 // link it
2281 - wt->next = wu->threads;
2282 - wu->threads = wt;
2285 + DOUBLE_LINKED_LIST_APPEND_UNSAFE(wu->threads, wt, prev, next);
2286
2287 return wt;
2288 }
@@ -2295,6 +2298,7 @@ static struct worker_thread *worker_thread_find_or_create(struct worker_utilizat
2298 static void worker_utilization_charts_callback(void *ptr
2299 , pid_t pid __maybe_unused
2300 , const char *thread_tag __maybe_unused
2301 + , size_t max_job_id __maybe_unused
2302 , size_t utilization_usec __maybe_unused
2303 , size_t duration_usec __maybe_unused
2304 , size_t jobs_started __maybe_unused
@@ -2311,7 +2315,7 @@ static void worker_utilization_charts_callback(void *ptr
2315 // find the worker_thread in the list
2316 struct worker_thread *wt = worker_thread_find_or_create(wu, pid);
2317
2314 - wt->enabled = 1;
2318 + wt->enabled = true;
2319 wt->busy_time = utilization_usec;
2320 wt->jobs_started = jobs_started;
2321
@@ -2319,6 +2323,9 @@ static void worker_utilization_charts_callback(void *ptr
2323 wt->stime_old = wt->stime;
2324 wt->collected_time_old = wt->collected_time;
2325
2326 + if(max_job_id > wu->workers_max_job_id)
2327 + wu->workers_max_job_id = max_job_id;
2328 +
2329 wu->workers_total_busy_time += utilization_usec;
2330 wu->workers_total_duration += duration_usec;
2331 wu->workers_total_jobs_started += jobs_started;
@@ -2334,7 +2341,7 @@ static void worker_utilization_charts_callback(void *ptr
2341
2342 // accumulate per job type statistics
2343 size_t i;
2337 - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
2344 + for(i = 0; i <= max_job_id ;i++) {
2345 if(!wu->per_job_type[i].name && job_types_names[i])
2346 wu->per_job_type[i].name = string_dup(job_types_names[i]);
2347
@@ -2372,13 +2379,13 @@ static void worker_utilization_charts_callback(void *ptr
2379 double stime = (double)(wt->stime - wt->stime_old) / (double)system_hz * 100.0 * (double)USEC_PER_SEC / (double)delta;
2380 double cpu = utime + stime;
2381 wt->cpu = cpu;
2375 - wt->cpu_enabled = 1;
2382 + wt->cpu_enabled = true;
2383
2384 wu->workers_cpu_total += cpu;
2385 if(cpu < wu->workers_cpu_min) wu->workers_cpu_min = cpu;
2386 if(cpu > wu->workers_cpu_max) wu->workers_cpu_max = cpu;
2387 }
2381 - wu->workers_cpu_registered += wt->cpu_enabled;
2388 + wu->workers_cpu_registered += (wt->cpu_enabled) ? 1 : 0;
2389 }
2390
2391 static void worker_utilization_charts(void) {
@@ -2420,7 +2427,8 @@ static void worker_utilization_finish(void) {
2427
2428 // mark all threads as not enabled
2429 struct worker_thread *t;
2423 - for(t = wu->threads; t ; t = t->next) t->enabled = 0;
2430 + for(t = wu->threads; t ; t = t->next)
2431 + t->enabled = false;
2432
2433 // let the cleanup job free them
2434 workers_threads_cleanup(wu);
libnetdata/libnetdata.h
+1
@@ -421,6 +421,7 @@ bool run_command_and_copy_output_to_stdout(const char *command, int max_line_len
421 void netdata_cleanup_and_exit(int ret) NORETURN;
422 void send_statistics(const char *action, const char *action_result, const char *action_data);
423 extern char *netdata_configured_host_prefix;
424 +#include "libjudy/src/Judy.h"
425 #include "os.h"
426 #include "storage_number/storage_number.h"
427 #include "threads/threads.h"
libnetdata/worker_utilization/worker_utilization.c
+43 -16
@@ -24,7 +24,6 @@ struct worker {
24 pid_t pid;
25 const char *tag;
26 const char *workname;
27 - uint32_t workname_hash;
27
28 // statistics controlled variables
29 volatile usec_t statistics_last_checkpoint;
@@ -32,6 +31,7 @@ struct worker {
31 usec_t statistics_last_busy_time;
32
33 // the worker controlled variables
34 + size_t worker_max_job_id;
35 volatile size_t job_id;
36 volatile size_t jobs_started;
37 volatile usec_t busy_time;
@@ -44,9 +44,9 @@ struct worker {
44 struct worker *prev;
45 };
46
47 -static netdata_mutex_t base_lock = NETDATA_MUTEX_INITIALIZER;
48 -static struct worker *base = NULL;
47 +static netdata_mutex_t workers_base_lock = NETDATA_MUTEX_INITIALIZER;
48 static __thread struct worker *worker = NULL;
49 +static Pvoid_t workers_per_workname_JudyHS_array = NULL;
50
51 void worker_register(const char *workname) {
52 if(unlikely(worker)) return;
@@ -55,16 +55,24 @@ void worker_register(const char *workname) {
55 worker->pid = gettid();
56 worker->tag = strdupz(netdata_thread_tag());
57 worker->workname = strdupz(workname);
58 - worker->workname_hash = simple_hash(worker->workname);
58
59 usec_t now = now_realtime_usec();
60 worker->statistics_last_checkpoint = now;
61 worker->last_action_timestamp = now;
62 worker->last_action = WORKER_IDLE;
63
65 - netdata_mutex_lock(&base_lock);
66 - DOUBLE_LINKED_LIST_PREPEND_UNSAFE(base, worker, prev, next);
67 - netdata_mutex_unlock(&base_lock);
64 + size_t workname_size = strlen(workname) + 1;
65 + netdata_mutex_lock(&workers_base_lock);
66 +
67 + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)workname, workname_size);
68 + if(!PValue)
69 + PValue = JudyHSIns(&workers_per_workname_JudyHS_array, (void *)workname, workname_size, PJE0);
70 +
71 + struct worker *base = *PValue;
72 + DOUBLE_LINKED_LIST_APPEND_UNSAFE(base, worker, prev, next);
73 + *PValue = base;
74 +
75 + netdata_mutex_unlock(&workers_base_lock);
76 }
77
78 void worker_register_job_custom_metric(size_t job_id, const char *name, const char *units, WORKER_METRIC_TYPE type) {
@@ -74,6 +82,10 @@ void worker_register_job_custom_metric(size_t job_id, const char *name, const ch
82 error("WORKER_UTILIZATION: job_id %zu is too big. Max is %zu", job_id, (size_t)(WORKER_UTILIZATION_MAX_JOB_TYPES - 1));
83 return;
84 }
85 +
86 + if(job_id > worker->worker_max_job_id)
87 + worker->worker_max_job_id = job_id;
88 +
89 if(worker->per_job_type[job_id].name) {
90 if(strcmp(string2str(worker->per_job_type[job_id].name), name) != 0 || worker->per_job_type[job_id].type != type || strcmp(string2str(worker->per_job_type[job_id].units), units) != 0)
91 error("WORKER_UTILIZATION: duplicate job registration: worker '%s' job id %zu is '%s', ignoring the later '%s'", worker->workname, job_id, string2str(worker->per_job_type[job_id].name), name);
@@ -92,9 +104,18 @@ void worker_register_job_name(size_t job_id, const char *name) {
104 void worker_unregister(void) {
105 if(unlikely(!worker)) return;
106
95 - netdata_mutex_lock(&base_lock);
96 - DOUBLE_LINKED_LIST_REMOVE_UNSAFE(base, worker, prev, next);
97 - netdata_mutex_unlock(&base_lock);
107 + size_t workname_size = strlen(worker->workname) + 1;
108 + netdata_mutex_lock(&workers_base_lock);
109 + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)worker->workname, workname_size);
110 + if(PValue) {
111 + struct worker *base = *PValue;
112 + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(base, worker, prev, next);
113 + *PValue = base;
114 +
115 + if(!base)
116 + JudyHSDel(&workers_per_workname_JudyHS_array, (void *)worker->workname, workname_size, PJE0);
117 + }
118 + netdata_mutex_unlock(&workers_base_lock);
119
120 for(int i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
121 string_freez(worker->per_job_type[i].name);
@@ -170,6 +191,7 @@ void workers_foreach(const char *workname, void (*callback)(
191 void *data
192 , pid_t pid
193 , const char *thread_tag
194 + , size_t max_job_id
195 , size_t utilization_usec
196 , size_t duration_usec
197 , size_t jobs_started, size_t is_running
@@ -181,15 +203,18 @@ void workers_foreach(const char *workname, void (*callback)(
203 , NETDATA_DOUBLE *job_custom_values
204 )
205 , void *data) {
184 - netdata_mutex_lock(&base_lock);
185 - uint32_t hash = simple_hash(workname);
206 + netdata_mutex_lock(&workers_base_lock);
207 usec_t busy_time, delta;
208 size_t i, jobs_started, jobs_running;
209
210 + size_t workname_size = strlen(workname) + 1;
211 + struct worker *base = NULL;
212 + Pvoid_t *PValue = JudyHSGet(workers_per_workname_JudyHS_array, (void *)workname, workname_size);
213 + if(PValue)
214 + base = *PValue;
215 +
216 struct worker *p;
217 DOUBLE_LINKED_LIST_FOREACH_FORWARD(base, p, prev, next) {
191 - if(hash != p->workname_hash || strcmp(workname, p->workname) != 0) continue;
192 -
218 usec_t now = now_realtime_usec();
219
220 // find per job type statistics
@@ -200,7 +225,8 @@ void workers_foreach(const char *workname, void (*callback)(
225 usec_t per_job_type_busy_time[WORKER_UTILIZATION_MAX_JOB_TYPES];
226 NETDATA_DOUBLE per_job_custom_values[WORKER_UTILIZATION_MAX_JOB_TYPES];
227
203 - for(i = 0; i < WORKER_UTILIZATION_MAX_JOB_TYPES ;i++) {
228 + size_t max_job_id = p->worker_max_job_id;
229 + for(i = 0; i <= max_job_id ;i++) {
230 per_job_type_name[i] = p->per_job_type[i].name;
231 per_job_type_units[i] = p->per_job_type[i].units;
232 per_job_metric_type[i] = p->per_job_type[i].type;
@@ -286,6 +312,7 @@ void workers_foreach(const char *workname, void (*callback)(
312 callback(data
313 , p->pid
314 , p->tag
315 + , max_job_id
316 , busy_time
317 , delta
318 , jobs_started
@@ -299,5 +326,5 @@ void workers_foreach(const char *workname, void (*callback)(
326 );
327 }
328
302 - netdata_mutex_unlock(&base_lock);
329 + netdata_mutex_unlock(&workers_base_lock);
330 }
libnetdata/worker_utilization/worker_utilization.h
+1
@@ -30,6 +30,7 @@ void workers_foreach(const char *workname, void (*callback)(
30 void *data
31 , pid_t pid
32 , const char *thread_tag
33 + , size_t max_job_id
34 , size_t utilization_usec
35 , size_t duration_usec
36 , size_t jobs_started