@cryptotaxi247 / netdata-1 / commits / 6ee8ea23f

Limit atomic operations for statistics (#15887)

Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>

Costa Tsaousis committed Sep 1, 2023 at 15:38 UTC 6ee8ea23f32be1ce0e373f0c3822e415edd2db03
4 files changed +207 -81
daemon/global_statistics.c
+23 -8
@@ -2681,9 +2681,12 @@ static void dbengine2_statistics_charts(void) {
2681
2682 static void update_strings_charts() {
2683 static RRDSET *st_ops = NULL, *st_entries = NULL, *st_mem = NULL;
2684 - static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL, *rd_ops_searches = NULL, *rd_ops_duplications = NULL, *rd_ops_releases = NULL;
2685 - static RRDDIM *rd_entries_entries = NULL, *rd_entries_refs = NULL;
2684 + static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL;
2685 + static RRDDIM *rd_entries_entries = NULL;
2686 static RRDDIM *rd_mem = NULL;
2687 +#ifdef NETDATA_INTERNAL_CHECKS
2688 + static RRDDIM *rd_entries_refs = NULL, *rd_ops_releases = NULL, *rd_ops_duplications = NULL, *rd_ops_searches = NULL;
2689 +#endif
2690
2691 size_t inserts, deletes, searches, entries, references, memory, duplications, releases;
2692
@@ -2706,16 +2709,20 @@ static void update_strings_charts() {
2709
2710 rd_ops_inserts = rrddim_add(st_ops, "inserts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2711 rd_ops_deletes = rrddim_add(st_ops, "deletes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2712 +#ifdef NETDATA_INTERNAL_CHECKS
2713 rd_ops_searches = rrddim_add(st_ops, "searches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2714 rd_ops_duplications = rrddim_add(st_ops, "duplications", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL);
2715 rd_ops_releases = rrddim_add(st_ops, "releases", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL);
2716 +#endif
2717 }
2718
2719 rrddim_set_by_pointer(st_ops, rd_ops_inserts, (collected_number)inserts);
2720 rrddim_set_by_pointer(st_ops, rd_ops_deletes, (collected_number)deletes);
2721 +#ifdef NETDATA_INTERNAL_CHECKS
2722 rrddim_set_by_pointer(st_ops, rd_ops_searches, (collected_number)searches);
2723 rrddim_set_by_pointer(st_ops, rd_ops_duplications, (collected_number)duplications);
2724 rrddim_set_by_pointer(st_ops, rd_ops_releases, (collected_number)releases);
2725 +#endif
2726 rrdset_done(st_ops);
2727
2728 if (unlikely(!st_entries)) {
@@ -2734,11 +2741,15 @@ static void update_strings_charts() {
2741 , RRDSET_TYPE_AREA);
2742
2743 rd_entries_entries = rrddim_add(st_entries, "entries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
2744 +#ifdef NETDATA_INTERNAL_CHECKS
2745 rd_entries_refs = rrddim_add(st_entries, "references", NULL, 1, -1, RRD_ALGORITHM_ABSOLUTE);
2746 +#endif
2747 }
2748
2749 rrddim_set_by_pointer(st_entries, rd_entries_entries, (collected_number)entries);
2750 +#ifdef NETDATA_INTERNAL_CHECKS
2751 rrddim_set_by_pointer(st_entries, rd_entries_refs, (collected_number)references);
2752 +#endif
2753 rrdset_done(st_entries);
2754
2755 if (unlikely(!st_mem)) {
@@ -2813,6 +2824,7 @@ struct dictionary_stats dictionary_stats_category_rrdhealth = { .name = "health"
2824 struct dictionary_stats dictionary_stats_category_functions = { .name = "functions" };
2825 struct dictionary_stats dictionary_stats_category_replication = { .name = "replication" };
2826
2827 +#ifdef DICT_WITH_STATS
2828 struct dictionary_categories {
2829 struct dictionary_stats *stats;
2830 const char *family;
@@ -3165,6 +3177,13 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) {
3177 }
3178 }
3179
3180 +static void dictionary_statistics(void) {
3181 + for(int i = 0; dictionary_categories[i].stats ;i++) {
3182 + update_dictionary_category_charts(&dictionary_categories[i]);
3183 + }
3184 +}
3185 +#endif // DICT_WITH_STATS
3186 +
3187 #ifdef NETDATA_TRACE_ALLOCATIONS
3188
3189 struct memory_trace_data {
@@ -3304,12 +3323,6 @@ static void malloc_trace_statistics(void) {
3323 }
3324 #endif
3325
3307 -static void dictionary_statistics(void) {
3308 - for(int i = 0; dictionary_categories[i].stats ;i++) {
3309 - update_dictionary_category_charts(&dictionary_categories[i]);
3310 - }
3311 -}
3312 -
3326 // ---------------------------------------------------------------------------------------------------------------------
3327 // worker utilization
3328
@@ -4171,8 +4184,10 @@ void *global_statistics_main(void *ptr)
4184 worker_is_busy(WORKER_JOB_STRINGS);
4185 update_strings_charts();
4186
4187 +#ifdef DICT_WITH_STATS
4188 worker_is_busy(WORKER_JOB_DICTIONARIES);
4189 dictionary_statistics();
4190 +#endif
4191
4192 #ifdef NETDATA_TRACE_ALLOCATIONS
4193 worker_is_busy(WORKER_JOB_MALLOC_TRACE);
libnetdata/dictionary/dictionary.c
+152 -60
@@ -250,6 +250,7 @@ static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM
250 // ----------------------------------------------------------------------------
251 // memory statistics
252
253 +#ifdef DICT_WITH_STATS
254 static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) {
255 if(key_size)
256 __atomic_fetch_add(&dict->stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
@@ -260,6 +261,7 @@ static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_siz
261 if(value_size)
262 __atomic_fetch_add(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
263 }
264 +
265 static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) {
266 if(key_size)
267 __atomic_fetch_sub(&dict->stats->memory.index, (long)JUDYHS_INDEX_SIZE_ESTIMATE(key_size), __ATOMIC_RELAXED);
@@ -270,6 +272,10 @@ static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_si
272 if(value_size)
273 __atomic_fetch_sub(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED);
274 }
275 +#else
276 +#define DICTIONARY_STATS_PLUS_MEMORY(dict, key_size, item_size, value_size) do {;} while(0)
277 +#define DICTIONARY_STATS_MINUS_MEMORY(dict, key_size, item_size, value_size) do {;} while(0)
278 +#endif
279
280 // ----------------------------------------------------------------------------
281 // callbacks registration
@@ -376,14 +382,21 @@ void dictionary_version_increment(DICTIONARY *dict) {
382 // ----------------------------------------------------------------------------
383 // internal statistics API
384
385 +#ifdef DICT_WITH_STATS
386 static inline void DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
387 __atomic_fetch_add(&dict->stats->ops.searches, 1, __ATOMIC_RELAXED);
388 }
389 +#else
390 +#define DICTIONARY_STATS_SEARCHES_PLUS1(dict) do {;} while(0)
391 +#endif
392 +
393 static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) {
394 +#ifdef DICT_WITH_STATS
395 // statistics
396 __atomic_fetch_add(&dict->stats->items.entries, 1, __ATOMIC_RELAXED);
397 __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
398 __atomic_fetch_add(&dict->stats->ops.inserts, 1, __ATOMIC_RELAXED);
399 +#endif
400
401 if(unlikely(is_dictionary_single_threaded(dict))) {
402 dict->version++;
@@ -397,10 +410,13 @@ static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) {
410 __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_RELAXED);
411 }
412 }
413 +
414 static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
415 +#ifdef DICT_WITH_STATS
416 // statistics
417 __atomic_fetch_add(&dict->stats->ops.deletes, 1, __ATOMIC_RELAXED);
418 __atomic_fetch_sub(&dict->stats->items.entries, 1, __ATOMIC_RELAXED);
419 +#endif
420
421 size_t entries; (void)entries;
422 if(unlikely(is_dictionary_single_threaded(dict))) {
@@ -418,14 +434,19 @@ static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) {
434 dict->creation_line,
435 dict->creation_file);
436 }
437 +
438 static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) {
439 +#ifdef DICT_WITH_STATS
440 __atomic_fetch_add(&dict->stats->ops.resets, 1, __ATOMIC_RELAXED);
441 +#endif
442
443 if(unlikely(is_dictionary_single_threaded(dict)))
444 dict->version++;
445 else
446 __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED);
447 }
448 +
449 +#ifdef DICT_WITH_STATS
450 static inline void DICTIONARY_STATS_TRAVERSALS_PLUS1(DICTIONARY *dict) {
451 __atomic_fetch_add(&dict->stats->ops.traversals, 1, __ATOMIC_RELAXED);
452 }
@@ -476,9 +497,29 @@ static inline void DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(DICTIONARY *dict)
497 static inline void DICTIONARY_STATS_DICT_FLUSHES_PLUS1(DICTIONARY *dict) {
498 __atomic_fetch_add(&dict->stats->ops.flushes, 1, __ATOMIC_RELAXED);
499 }
500 +#else
501 +#define DICTIONARY_STATS_TRAVERSALS_PLUS1(dict) do {;} while(0)
502 +#define DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict) do {;} while(0)
503 +#define DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, count) do {;} while(0)
504 +#define DICTIONARY_STATS_INSERT_SPINS_PLUS(dict, count) do {;} while(0)
505 +#define DICTIONARY_STATS_DELETE_SPINS_PLUS(dict, count) do {;} while(0)
506 +#define DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(dict) do {;} while(0)
507 +#define DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict) do {;} while(0)
508 +#define DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict) do {;} while(0)
509 +#define DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict) do {;} while(0)
510 +#define DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict) do {;} while(0)
511 +#define DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(dict) do {;} while(0)
512 +#define DICTIONARY_STATS_DICT_CREATIONS_PLUS1(dict) do {;} while(0)
513 +#define DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(dict) do {;} while(0)
514 +#define DICTIONARY_STATS_DICT_DESTROY_QUEUED_PLUS1(dict) do {;} while(0)
515 +#define DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(dict) do {;} while(0)
516 +#define DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict) do {;} while(0)
517 +#endif
518
519 static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
520 +#ifdef DICT_WITH_STATS
521 __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
522 +#endif
523
524 if(unlikely(is_dictionary_single_threaded(dict)))
525 ++dict->referenced_items;
@@ -487,7 +528,9 @@ static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
528 }
529
530 static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
531 +#ifdef DICT_WITH_STATS
532 __atomic_fetch_sub(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED);
533 +#endif
534
535 long int referenced_items; (void)referenced_items;
536 if(unlikely(is_dictionary_single_threaded(dict)))
@@ -504,7 +547,9 @@ static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
547 }
548
549 static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
550 +#ifdef DICT_WITH_STATS
551 __atomic_fetch_add(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED);
552 +#endif
553
554 if(unlikely(is_dictionary_single_threaded(dict)))
555 ++dict->pending_deletion_items;
@@ -513,7 +558,9 @@ static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
558 }
559
560 static inline long int DICTIONARY_PENDING_DELETES_MINUS1(DICTIONARY *dict) {
561 +#ifdef DICT_WITH_STATS
562 __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELEASE);
563 +#endif
564
565 if(unlikely(is_dictionary_single_threaded(dict)))
566 return --dict->pending_deletion_items;
@@ -977,7 +1024,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it
1024 DICTIONARY_REFERENCED_ITEMS_PLUS1(dict);
1025 }
1026
980 - if(unlikely(spins > 1 && dict->stats))
1027 + if(unlikely(spins > 1))
1028 DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, spins - 1);
1029
1030 return ret;
@@ -1022,7 +1069,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY
1069 item->deleter_pid = gettid();
1070 #endif
1071
1025 - if(unlikely(spins > 1 && dict->stats))
1072 + if(unlikely(spins > 1))
1073 DICTIONARY_STATS_DELETE_SPINS_PLUS(dict, spins - 1);
1074
1075 return ret;
@@ -1535,22 +1582,6 @@ static inline void dict_item_release_and_check_if_it_is_deleted_and_can_be_remov
1582 }
1583
1584 static bool dict_item_del(DICTIONARY *dict, const char *name, ssize_t name_len) {
1538 - if(unlikely(!name || !*name)) {
1539 - internal_error(
1540 - true,
1541 - "DICTIONARY: attempted to %s() without a name on a dictionary created from %s() %zu@%s.",
1542 - __FUNCTION__,
1543 - dict->creation_function,
1544 - dict->creation_line,
1545 - dict->creation_file);
1546 - return false;
1547 - }
1548 -
1549 - if(unlikely(is_dictionary_destroyed(dict))) {
1550 - internal_error(true, "DICTIONARY: attempted to dictionary_del() on a destroyed dictionary");
1551 - return false;
1552 - }
1553 -
1585 if(name_len == -1)
1586 name_len = (ssize_t)strlen(name) + 1; // we need the terminating null too
1587
@@ -1695,7 +1726,7 @@ static DICTIONARY_ITEM *dict_item_add_or_reset_value_and_acquire(DICTIONARY *dic
1726 } while(!item);
1727
1728
1698 - if(unlikely(spins > 0 && dict->stats))
1729 + if(unlikely(spins > 0))
1730 DICTIONARY_STATS_INSERT_SPINS_PLUS(dict, spins);
1731
1732 if(is_master_dictionary(dict) && added_or_updated)
@@ -2064,11 +2095,15 @@ void dictionary_flush(DICTIONARY *dict) {
2095 if(unlikely(!dict))
2096 return;
2097
2067 - void *value;
2068 - dfe_start_write(dict, value) {
2069 - dictionary_del_advanced(dict, item_get_name(value_dfe.item), (ssize_t)item_get_name_len(value_dfe.item) + 1);
2098 + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE);
2099 +
2100 + DICTIONARY_ITEM *item, *next = NULL;
2101 + for(item = dict->items.list; item ;item = next) {
2102 + next = item->next;
2103 + dict_item_del(dict, item_get_name(item), (ssize_t) item_get_name_len(item) + 1);
2104 }
2071 - dfe_done(value);
2105 +
2106 + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE);
2107
2108 DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict);
2109 }
@@ -2251,6 +2286,12 @@ bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_le
2286 return false;
2287
2288 api_internal_check(dict, NULL, false, true);
2289 +
2290 + if(unlikely(is_dictionary_destroyed(dict))) {
2291 + internal_error(true, "DICTIONARY: attempted to delete item on a destroyed dictionary");
2292 + return false;
2293 + }
2294 +
2295 return dict_item_del(dict, name, name_len);
2296 }
2297
@@ -2260,6 +2301,8 @@ bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_le
2301 void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
2302 if(unlikely(!dfe || !dict)) return NULL;
2303
2304 + DICTIONARY_STATS_TRAVERSALS_PLUS1(dict);
2305 +
2306 if(unlikely(is_dictionary_destroyed(dict))) {
2307 internal_error(true, "DICTIONARY: attempted to dictionary_foreach_start_rw() on a destroyed dictionary");
2308 dfe->counter = 0;
@@ -2275,8 +2318,6 @@ void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
2318 dfe->locked = true;
2319 ll_recursive_lock(dict, dfe->rw);
2320
2278 - DICTIONARY_STATS_TRAVERSALS_PLUS1(dict);
2279 -
2321 // get the first item from the list
2322 DICTIONARY_ITEM *item = dict->items.list;
2323
@@ -3129,6 +3170,9 @@ struct thread_unittest {
3170 int join;
3171 DICTIONARY *dict;
3172 int dups;
3173 +
3174 + netdata_thread_t thread;
3175 + struct dictionary_stats stats;
3176 };
3177
3178 static void *unittest_dict_thread(void *arg) {
@@ -3140,46 +3184,59 @@ static void *unittest_dict_thread(void *arg) {
3184 DICT_ITEM_CONST DICTIONARY_ITEM *item =
3185 dictionary_set_and_acquire_item_advanced(tu->dict, "dict thread checking 1234567890",
3186 -1, NULL, 0, NULL);
3143 -
3187 + tu->stats.ops.inserts++;
3188
3189 dictionary_get(tu->dict, dictionary_acquired_item_name(item));
3190 + tu->stats.ops.searches++;
3191
3192 void *t1;
3193 dfe_start_write(tu->dict, t1) {
3194
3195 // this should delete the referenced item
3196 dictionary_del(tu->dict, t1_dfe.name);
3197 + tu->stats.ops.deletes++;
3198
3199 void *t2;
3200 dfe_start_write(tu->dict, t2) {
3201 // this should add another
3202 dictionary_set(tu->dict, t2_dfe.name, NULL, 0);
3203 + tu->stats.ops.inserts++;
3204
3205 dictionary_get(tu->dict, dictionary_acquired_item_name(item));
3206 + tu->stats.ops.searches++;
3207
3208 // and this should delete it again
3209 dictionary_del(tu->dict, t2_dfe.name);
3210 + tu->stats.ops.deletes++;
3211 }
3212 dfe_done(t2);
3213 + tu->stats.ops.traversals++;
3214
3215 // this should fail to add it
3216 dictionary_set(tu->dict, t1_dfe.name, NULL, 0);
3217 + tu->stats.ops.inserts++;
3218 +
3219 dictionary_del(tu->dict, t1_dfe.name);
3220 + tu->stats.ops.deletes++;
3221 }
3222 dfe_done(t1);
3223 + tu->stats.ops.traversals++;
3224
3225 for(int i = 0; i < tu->dups ; i++) {
3226 dictionary_acquired_item_dup(tu->dict, item);
3227 dictionary_get(tu->dict, dictionary_acquired_item_name(item));
3228 + tu->stats.ops.searches++;
3229 }
3230
3231 for(int i = 0; i < tu->dups ; i++) {
3232 dictionary_acquired_item_release(tu->dict, item);
3233 dictionary_del(tu->dict, dictionary_acquired_item_name(item));
3234 + tu->stats.ops.deletes++;
3235 }
3236
3237 dictionary_acquired_item_release(tu->dict, item);
3238 dictionary_del(tu->dict, "dict thread checking 1234567890");
3239 + tu->stats.ops.deletes++;
3240
3241 // test concurrent deletions and flushes
3242 {
@@ -3189,16 +3246,19 @@ static void *unittest_dict_thread(void *arg) {
3246 for (int i = 0; i < 1000; i++) {
3247 snprintfz(buf, 256, "del/flush test %d", i);
3248 dictionary_set(tu->dict, buf, NULL, 0);
3249 + tu->stats.ops.inserts++;
3250 }
3251
3252 for (int i = 0; i < 1000; i++) {
3253 snprintfz(buf, 256, "del/flush test %d", i);
3254 dictionary_del(tu->dict, buf);
3255 + tu->stats.ops.deletes++;
3256 }
3257 }
3258 else {
3259 for (int i = 0; i < 10; i++) {
3260 dictionary_flush(tu->dict);
3261 + tu->stats.ops.flushes++;
3262 }
3263 }
3264 }
@@ -3208,47 +3268,75 @@ static void *unittest_dict_thread(void *arg) {
3268 }
3269
3270 static int dictionary_unittest_threads() {
3211 -
3212 - struct thread_unittest tu = {
3213 - .join = 0,
3214 - .dict = NULL,
3215 - .dups = 1,
3216 - };
3217 -
3218 - // threads testing of dictionary
3219 - tu.dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE);
3271 time_t seconds_to_run = 5;
3272 int threads_to_create = 2;
3273 +
3274 + struct thread_unittest tu[threads_to_create];
3275 + memset(tu, 0, sizeof(struct thread_unittest) * threads_to_create);
3276 +
3277 fprintf(
3223 - stderr,
3224 - "\nChecking dictionary concurrency with %d threads for %lld seconds...\n",
3225 - threads_to_create,
3226 - (long long)seconds_to_run);
3278 + stderr,
3279 + "\nChecking dictionary concurrency with %d threads for %lld seconds...\n",
3280 + threads_to_create,
3281 + (long long)seconds_to_run);
3282 +
3283 + // threads testing of dictionary
3284 + struct dictionary_stats stats = {};
3285 + tu[0].join = 0;
3286 + tu[0].dups = 1;
3287 + tu[0].dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE, &stats, 0);
3288
3228 - netdata_thread_t threads[threads_to_create];
3229 - tu.join = 0;
3289 for (int i = 0; i < threads_to_create; i++) {
3290 + if(i)
3291 + tu[i] = tu[0];
3292 +
3293 char buf[100 + 1];
3294 snprintf(buf, 100, "dict%d", i);
3295 netdata_thread_create(
3234 - &threads[i],
3296 + &tu[i].thread,
3297 buf,
3298 NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE,
3299 unittest_dict_thread,
3238 - &tu);
3300 + &tu[i]);
3301 }
3302 +
3303 sleep_usec(seconds_to_run * USEC_PER_SEC);
3304
3242 - __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED);
3305 for (int i = 0; i < threads_to_create; i++) {
3306 + __atomic_store_n(&tu[i].join, 1, __ATOMIC_RELAXED);
3307 +
3308 void *retval;
3245 - netdata_thread_join(threads[i], &retval);
3309 + netdata_thread_join(tu[i].thread, &retval);
3310 +
3311 + if(i) {
3312 + tu[0].stats.ops.inserts += tu[i].stats.ops.inserts;
3313 + tu[0].stats.ops.deletes += tu[i].stats.ops.deletes;
3314 + tu[0].stats.ops.searches += tu[i].stats.ops.searches;
3315 + tu[0].stats.ops.flushes += tu[i].stats.ops.flushes;
3316 + tu[0].stats.ops.traversals += tu[i].stats.ops.traversals;
3317 + }
3318 }
3319
3320 fprintf(stderr,
3249 - "inserts %zu"
3321 + "CALLS : inserts %zu"
3322 ", deletes %zu"
3323 ", searches %zu"
3324 + ", traversals %zu"
3325 + ", flushes %zu"
3326 + "\n",
3327 + tu[0].stats.ops.inserts,
3328 + tu[0].stats.ops.deletes,
3329 + tu[0].stats.ops.searches,
3330 + tu[0].stats.ops.traversals,
3331 + tu[0].stats.ops.flushes
3332 + );
3333 +
3334 +#ifdef DICT_WITH_STATS
3335 + fprintf(stderr,
3336 + "ACTUAL: inserts %zu"
3337 + ", deletes %zu"
3338 + ", searches %zu"
3339 + ", traversals %zu"
3340 ", resets %zu"
3341 ", flushes %zu"
3342 ", entries %d"
@@ -3259,22 +3347,23 @@ static int dictionary_unittest_threads() {
3347 ", delete spins %zu"
3348 ", search ignores %zu"
3349 "\n",
3262 - tu.dict->stats->ops.inserts,
3263 - tu.dict->stats->ops.deletes,
3264 - tu.dict->stats->ops.searches,
3265 - tu.dict->stats->ops.resets,
3266 - tu.dict->stats->ops.flushes,
3267 - tu.dict->entries,
3268 - tu.dict->referenced_items,
3269 - tu.dict->pending_deletion_items,
3270 - tu.dict->stats->spin_locks.use_spins,
3271 - tu.dict->stats->spin_locks.insert_spins,
3272 - tu.dict->stats->spin_locks.delete_spins,
3273 - tu.dict->stats->spin_locks.search_spins
3350 + stats.ops.inserts,
3351 + stats.ops.deletes,
3352 + stats.ops.searches,
3353 + stats.ops.traversals,
3354 + stats.ops.resets,
3355 + stats.ops.flushes,
3356 + tu[0].dict->entries,
3357 + tu[0].dict->referenced_items,
3358 + tu[0].dict->pending_deletion_items,
3359 + stats.spin_locks.use_spins,
3360 + stats.spin_locks.insert_spins,
3361 + stats.spin_locks.delete_spins,
3362 + stats.spin_locks.search_spins
3363 );
3275 - dictionary_destroy(tu.dict);
3276 - tu.dict = NULL;
3364 +#endif
3365
3366 + dictionary_destroy(tu[0].dict);
3367 return 0;
3368 }
3369
@@ -3407,6 +3496,7 @@ static int dictionary_unittest_view_threads() {
3496 netdata_thread_join(view_thread, &retval);
3497 netdata_thread_join(master_thread, &retval);
3498
3499 +#ifdef DICT_WITH_STATS
3500 fprintf(stderr,
3501 "MASTER: inserts %zu"
3502 ", deletes %zu"
@@ -3457,6 +3547,8 @@ static int dictionary_unittest_view_threads() {
3547 stats_view.spin_locks.delete_spins,
3548 stats_view.spin_locks.search_spins
3549 );
3550 +#endif
3551 +
3552 dictionary_destroy(tv.master);
3553 dictionary_destroy(tv.view);
3554
libnetdata/dictionary/dictionary.h
+5 -1
@@ -35,6 +35,10 @@
35 *
36 */
37
38 +#ifdef NETDATA_INTERNAL_CHECKS
39 +#define DICT_WITH_STATS 1
40 +#endif
41 +
42 #ifdef DICTIONARY_INTERNALS
43 #define DICTFE_CONST
44 #define DICT_ITEM_CONST
@@ -92,7 +96,7 @@ struct dictionary_stats {
96
97 // memory
98 struct {
95 - long index; // bytes of keys indexed (indication of the index size)
99 + long index; // bytes of keys indexed (indication of the index size)
100 long values; // bytes of caller structures
101 long dict; // bytes of the structures dictionary needs
102 } memory;
libnetdata/string/string.c
+27 -12
@@ -28,19 +28,22 @@ static struct string_partition {
28
29 Pvoid_t JudyHSArray; // the Judy array - hashtable
30
31 - size_t searches; // the number of successful searches in the index
32 - size_t duplications; // when a string is referenced
33 - size_t releases; // when a string is unreferenced
34 -
31 size_t inserts; // the number of successful inserts to the index
32 size_t deletes; // the number of successful deleted from the index
33
34 long int entries; // the number of entries in the index
39 - long int active_references; // the number of active references alive
35 long int memory; // the memory used, without the JudyHS index
36
37 #ifdef NETDATA_INTERNAL_CHECKS
38 // internal statistics
39 +
40 + struct {
41 + size_t searches; // the number of successful searches in the index
42 + size_t releases; // when a string is unreferenced
43 + size_t duplications; // when a string is referenced
44 + long int active_references; // the number of active references alive
45 + } atomic;
46 +
47 size_t found_deleted_on_search;
48 size_t found_available_on_search;
49 size_t found_deleted_on_insert;
@@ -51,14 +54,15 @@ static struct string_partition {
54 } string_base[STRING_PARTITIONS] = { 0 };
55
56 #ifdef NETDATA_INTERNAL_CHECKS
57 +#define string_stats_atomic_increment(partition, var) __atomic_add_fetch(&string_base[partition].atomic.var, 1, __ATOMIC_RELAXED)
58 +#define string_stats_atomic_decrement(partition, var) __atomic_sub_fetch(&string_base[partition].atomic.var, 1, __ATOMIC_RELAXED)
59 #define string_internal_stats_add(partition, var, val) __atomic_add_fetch(&string_base[partition].var, val, __ATOMIC_RELAXED)
60 #else
61 +#define string_stats_atomic_increment(partition, var) do {;} while(0)
62 +#define string_stats_atomic_decrement(partition, var) do {;} while(0)
63 #define string_internal_stats_add(partition, var, val) do {;} while(0)
64 #endif
65
59 -#define string_stats_atomic_increment(partition, var) __atomic_add_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED)
60 -#define string_stats_atomic_decrement(partition, var) __atomic_sub_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED)
61 -
66 void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_t *entries, size_t *references, size_t *memory, size_t *duplications, size_t *releases) {
67 if (inserts) *inserts = 0;
68 if (deletes) *deletes = 0;
@@ -72,12 +76,15 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
76 for(size_t i = 0; i < STRING_PARTITIONS ;i++) {
77 if (inserts) *inserts += string_base[i].inserts;
78 if (deletes) *deletes += string_base[i].deletes;
75 - if (searches) *searches += string_base[i].searches;
79 if (entries) *entries += (size_t) string_base[i].entries;
77 - if (references) *references += (size_t) string_base[i].active_references;
80 if (memory) *memory += (size_t) string_base[i].memory;
79 - if (duplications) *duplications += string_base[i].duplications;
80 - if (releases) *releases += string_base[i].releases;
81 +
82 +#ifdef NETDATA_INTERNAL_CHECKS
83 + if (searches) *searches += string_base[i].atomic.searches;
84 + if (references) *references += (size_t) string_base[i].atomic.active_references;
85 + if (duplications) *duplications += string_base[i].atomic.duplications;
86 + if (releases) *releases += string_base[i].atomic.releases;
87 +#endif
88 }
89 }
90
@@ -85,7 +92,9 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_
92 #define string_entry_release(se) __atomic_sub_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST);
93
94 static inline bool string_entry_check_and_acquire(STRING *se) {
95 +#ifdef NETDATA_INTERNAL_CHECKS
96 uint8_t partition = string_partition(se);
97 +#endif
98
99 REFCOUNT expected, desired, count = 0;
100
@@ -125,7 +134,9 @@ STRING *string_dup(STRING *string) {
134
135 string_entry_acquire(string);
136
137 +#ifdef NETDATA_INTERNAL_CHECKS
138 uint8_t partition = string_partition(string);
139 +#endif
140
141 // statistics
142 string_stats_atomic_increment(partition, active_references);
@@ -275,7 +286,9 @@ static inline void string_index_delete(STRING *string) {
286 STRING *string_strdupz(const char *str) {
287 if(unlikely(!str || !*str)) return NULL;
288
289 +#ifdef NETDATA_INTERNAL_CHECKS
290 uint8_t partition = string_partition_str(str);
291 +#endif
292
293 size_t length = strlen(str) + 1;
294 STRING *string = string_index_search(str, length);
@@ -297,7 +310,9 @@ STRING *string_strdupz(const char *str) {
310 void string_freez(STRING *string) {
311 if(unlikely(!string)) return;
312
313 +#ifdef NETDATA_INTERNAL_CHECKS
314 uint8_t partition = string_partition(string);
315 +#endif
316 REFCOUNT refcount = string_entry_release(string);
317
318 #ifdef NETDATA_INTERNAL_CHECKS