diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2023-09-01 15:38:09 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-01 15:38:09 +0300 |
commit | 6ee8ea23f32be1ce0e373f0c3822e415edd2db03 (patch) | |
tree | baeb745e3debf1858494d5688968fc6a9530916f | |
parent | 24006ed5c19cf7c398cf8a76e7bdce1f60c7e5a4 (diff) |
Limit atomic operations for statistics (#15887)
Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>
-rw-r--r-- | daemon/global_statistics.c | 31 | ||||
-rw-r--r-- | libnetdata/dictionary/dictionary.c | 212 | ||||
-rw-r--r-- | libnetdata/dictionary/dictionary.h | 6 | ||||
-rw-r--r-- | libnetdata/string/string.c | 39 |
4 files changed, 207 insertions, 81 deletions
diff --git a/daemon/global_statistics.c b/daemon/global_statistics.c index ce8d414026..ab910e1890 100644 --- a/daemon/global_statistics.c +++ b/daemon/global_statistics.c @@ -2681,9 +2681,12 @@ static void dbengine2_statistics_charts(void) { static void update_strings_charts() { static RRDSET *st_ops = NULL, *st_entries = NULL, *st_mem = NULL; - static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL, *rd_ops_searches = NULL, *rd_ops_duplications = NULL, *rd_ops_releases = NULL; - static RRDDIM *rd_entries_entries = NULL, *rd_entries_refs = NULL; + static RRDDIM *rd_ops_inserts = NULL, *rd_ops_deletes = NULL; + static RRDDIM *rd_entries_entries = NULL; static RRDDIM *rd_mem = NULL; +#ifdef NETDATA_INTERNAL_CHECKS + static RRDDIM *rd_entries_refs = NULL, *rd_ops_releases = NULL, *rd_ops_duplications = NULL, *rd_ops_searches = NULL; +#endif size_t inserts, deletes, searches, entries, references, memory, duplications, releases; @@ -2706,16 +2709,20 @@ static void update_strings_charts() { rd_ops_inserts = rrddim_add(st_ops, "inserts", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_ops_deletes = rrddim_add(st_ops, "deletes", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); +#ifdef NETDATA_INTERNAL_CHECKS rd_ops_searches = rrddim_add(st_ops, "searches", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_ops_duplications = rrddim_add(st_ops, "duplications", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); rd_ops_releases = rrddim_add(st_ops, "releases", NULL, -1, 1, RRD_ALGORITHM_INCREMENTAL); +#endif } rrddim_set_by_pointer(st_ops, rd_ops_inserts, (collected_number)inserts); rrddim_set_by_pointer(st_ops, rd_ops_deletes, (collected_number)deletes); +#ifdef NETDATA_INTERNAL_CHECKS rrddim_set_by_pointer(st_ops, rd_ops_searches, (collected_number)searches); rrddim_set_by_pointer(st_ops, rd_ops_duplications, (collected_number)duplications); rrddim_set_by_pointer(st_ops, rd_ops_releases, (collected_number)releases); +#endif rrdset_done(st_ops); if (unlikely(!st_entries)) { @@ -2734,11 +2741,15 @@ static void update_strings_charts() { , RRDSET_TYPE_AREA); rd_entries_entries = rrddim_add(st_entries, "entries", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); +#ifdef NETDATA_INTERNAL_CHECKS rd_entries_refs = rrddim_add(st_entries, "references", NULL, 1, -1, RRD_ALGORITHM_ABSOLUTE); +#endif } rrddim_set_by_pointer(st_entries, rd_entries_entries, (collected_number)entries); +#ifdef NETDATA_INTERNAL_CHECKS rrddim_set_by_pointer(st_entries, rd_entries_refs, (collected_number)references); +#endif rrdset_done(st_entries); if (unlikely(!st_mem)) { @@ -2813,6 +2824,7 @@ struct dictionary_stats dictionary_stats_category_rrdhealth = { .name = "health" struct dictionary_stats dictionary_stats_category_functions = { .name = "functions" }; struct dictionary_stats dictionary_stats_category_replication = { .name = "replication" }; +#ifdef DICT_WITH_STATS struct dictionary_categories { struct dictionary_stats *stats; const char *family; @@ -3165,6 +3177,13 @@ static void update_dictionary_category_charts(struct dictionary_categories *c) { } } +static void dictionary_statistics(void) { + for(int i = 0; dictionary_categories[i].stats ;i++) { + update_dictionary_category_charts(&dictionary_categories[i]); + } +} +#endif // DICT_WITH_STATS + #ifdef NETDATA_TRACE_ALLOCATIONS struct memory_trace_data { @@ -3304,12 +3323,6 @@ static void malloc_trace_statistics(void) { } #endif -static void dictionary_statistics(void) { - for(int i = 0; dictionary_categories[i].stats ;i++) { - update_dictionary_category_charts(&dictionary_categories[i]); - } -} - // --------------------------------------------------------------------------------------------------------------------- // worker utilization @@ -4171,8 +4184,10 @@ void *global_statistics_main(void *ptr) worker_is_busy(WORKER_JOB_STRINGS); update_strings_charts(); +#ifdef DICT_WITH_STATS worker_is_busy(WORKER_JOB_DICTIONARIES); dictionary_statistics(); +#endif #ifdef NETDATA_TRACE_ALLOCATIONS worker_is_busy(WORKER_JOB_MALLOC_TRACE); diff --git a/libnetdata/dictionary/dictionary.c b/libnetdata/dictionary/dictionary.c index 05da553444..1d695bae03 100644 --- a/libnetdata/dictionary/dictionary.c +++ b/libnetdata/dictionary/dictionary.c @@ -250,6 +250,7 @@ static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM // ---------------------------------------------------------------------------- // memory statistics +#ifdef DICT_WITH_STATS static inline void DICTIONARY_STATS_PLUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) { if(key_size) __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 if(value_size) __atomic_fetch_add(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED); } + static inline void DICTIONARY_STATS_MINUS_MEMORY(DICTIONARY *dict, size_t key_size, size_t item_size, size_t value_size) { if(key_size) __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 if(value_size) __atomic_fetch_sub(&dict->stats->memory.values, (long)value_size, __ATOMIC_RELAXED); } +#else +#define DICTIONARY_STATS_PLUS_MEMORY(dict, key_size, item_size, value_size) do {;} while(0) +#define DICTIONARY_STATS_MINUS_MEMORY(dict, key_size, item_size, value_size) do {;} while(0) +#endif // ---------------------------------------------------------------------------- // callbacks registration @@ -376,14 +382,21 @@ void dictionary_version_increment(DICTIONARY *dict) { // ---------------------------------------------------------------------------- // internal statistics API +#ifdef DICT_WITH_STATS static inline void DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) { __atomic_fetch_add(&dict->stats->ops.searches, 1, __ATOMIC_RELAXED); } +#else +#define DICTIONARY_STATS_SEARCHES_PLUS1(dict) do {;} while(0) +#endif + static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS // statistics __atomic_fetch_add(&dict->stats->items.entries, 1, __ATOMIC_RELAXED); __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); __atomic_fetch_add(&dict->stats->ops.inserts, 1, __ATOMIC_RELAXED); +#endif if(unlikely(is_dictionary_single_threaded(dict))) { dict->version++; @@ -397,10 +410,13 @@ static inline void DICTIONARY_ENTRIES_PLUS1(DICTIONARY *dict) { __atomic_fetch_add(&dict->referenced_items, 1, __ATOMIC_RELAXED); } } + static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS // statistics __atomic_fetch_add(&dict->stats->ops.deletes, 1, __ATOMIC_RELAXED); __atomic_fetch_sub(&dict->stats->items.entries, 1, __ATOMIC_RELAXED); +#endif size_t entries; (void)entries; if(unlikely(is_dictionary_single_threaded(dict))) { @@ -418,14 +434,19 @@ static inline void DICTIONARY_ENTRIES_MINUS1(DICTIONARY *dict) { dict->creation_line, dict->creation_file); } + static inline void DICTIONARY_VALUE_RESETS_PLUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS __atomic_fetch_add(&dict->stats->ops.resets, 1, __ATOMIC_RELAXED); +#endif if(unlikely(is_dictionary_single_threaded(dict))) dict->version++; else __atomic_fetch_add(&dict->version, 1, __ATOMIC_RELAXED); } + +#ifdef DICT_WITH_STATS static inline void DICTIONARY_STATS_TRAVERSALS_PLUS1(DICTIONARY *dict) { __atomic_fetch_add(&dict->stats->ops.traversals, 1, __ATOMIC_RELAXED); } @@ -476,9 +497,29 @@ static inline void DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(DICTIONARY *dict) static inline void DICTIONARY_STATS_DICT_FLUSHES_PLUS1(DICTIONARY *dict) { __atomic_fetch_add(&dict->stats->ops.flushes, 1, __ATOMIC_RELAXED); } +#else +#define DICTIONARY_STATS_TRAVERSALS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, count) do {;} while(0) +#define DICTIONARY_STATS_INSERT_SPINS_PLUS(dict, count) do {;} while(0) +#define DICTIONARY_STATS_DELETE_SPINS_PLUS(dict, count) do {;} while(0) +#define DICTIONARY_STATS_SEARCH_IGNORES_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_CALLBACK_INSERTS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_CALLBACK_CONFLICTS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_CALLBACK_REACTS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_CALLBACK_DELETES_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_GARBAGE_COLLECTIONS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_DICT_CREATIONS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_DICT_DESTROY_QUEUED_PLUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_DICT_DESTROY_QUEUED_MINUS1(dict) do {;} while(0) +#define DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict) do {;} while(0) +#endif static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS __atomic_fetch_add(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); +#endif if(unlikely(is_dictionary_single_threaded(dict))) ++dict->referenced_items; @@ -487,7 +528,9 @@ static inline void DICTIONARY_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) { } static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS __atomic_fetch_sub(&dict->stats->items.referenced, 1, __ATOMIC_RELAXED); +#endif long int referenced_items; (void)referenced_items; if(unlikely(is_dictionary_single_threaded(dict))) @@ -504,7 +547,9 @@ static inline void DICTIONARY_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) { } static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS __atomic_fetch_add(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELAXED); +#endif if(unlikely(is_dictionary_single_threaded(dict))) ++dict->pending_deletion_items; @@ -513,7 +558,9 @@ static inline void DICTIONARY_PENDING_DELETES_PLUS1(DICTIONARY *dict) { } static inline long int DICTIONARY_PENDING_DELETES_MINUS1(DICTIONARY *dict) { +#ifdef DICT_WITH_STATS __atomic_fetch_sub(&dict->stats->items.pending_deletion, 1, __ATOMIC_RELEASE); +#endif if(unlikely(is_dictionary_single_threaded(dict))) return --dict->pending_deletion_items; @@ -977,7 +1024,7 @@ static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *it DICTIONARY_REFERENCED_ITEMS_PLUS1(dict); } - if(unlikely(spins > 1 && dict->stats)) + if(unlikely(spins > 1)) DICTIONARY_STATS_CHECK_SPINS_PLUS(dict, spins - 1); return ret; @@ -1022,7 +1069,7 @@ static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY item->deleter_pid = gettid(); #endif - if(unlikely(spins > 1 && dict->stats)) + if(unlikely(spins > 1)) DICTIONARY_STATS_DELETE_SPINS_PLUS(dict, spins - 1); return ret; @@ -1535,22 +1582,6 @@ static inline void dict_item_release_and_check_if_it_is_deleted_and_can_be_remov } static bool dict_item_del(DICTIONARY *dict, const char *name, ssize_t name_len) { - if(unlikely(!name || !*name)) { - internal_error( - true, - "DICTIONARY: attempted to %s() without a name on a dictionary created from %s() %zu@%s.", - __FUNCTION__, - dict->creation_function, - dict->creation_line, - dict->creation_file); - return false; - } - - if(unlikely(is_dictionary_destroyed(dict))) { - internal_error(true, "DICTIONARY: attempted to dictionary_del() on a destroyed dictionary"); - return false; - } - if(name_len == -1) name_len = (ssize_t)strlen(name) + 1; // we need the terminating null too @@ -1695,7 +1726,7 @@ static DICTIONARY_ITEM *dict_item_add_or_reset_value_and_acquire(DICTIONARY *dic } while(!item); - if(unlikely(spins > 0 && dict->stats)) + if(unlikely(spins > 0)) DICTIONARY_STATS_INSERT_SPINS_PLUS(dict, spins); if(is_master_dictionary(dict) && added_or_updated) @@ -2064,11 +2095,15 @@ void dictionary_flush(DICTIONARY *dict) { if(unlikely(!dict)) return; - void *value; - dfe_start_write(dict, value) { - dictionary_del_advanced(dict, item_get_name(value_dfe.item), (ssize_t)item_get_name_len(value_dfe.item) + 1); + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); + + DICTIONARY_ITEM *item, *next = NULL; + for(item = dict->items.list; item ;item = next) { + next = item->next; + dict_item_del(dict, item_get_name(item), (ssize_t) item_get_name_len(item) + 1); } - dfe_done(value); + + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); DICTIONARY_STATS_DICT_FLUSHES_PLUS1(dict); } @@ -2251,6 +2286,12 @@ bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_le return false; api_internal_check(dict, NULL, false, true); + + if(unlikely(is_dictionary_destroyed(dict))) { + internal_error(true, "DICTIONARY: attempted to delete item on a destroyed dictionary"); + return false; + } + return dict_item_del(dict, name, name_len); } @@ -2260,6 +2301,8 @@ bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_le void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) { if(unlikely(!dfe || !dict)) return NULL; + DICTIONARY_STATS_TRAVERSALS_PLUS1(dict); + if(unlikely(is_dictionary_destroyed(dict))) { internal_error(true, "DICTIONARY: attempted to dictionary_foreach_start_rw() on a destroyed dictionary"); dfe->counter = 0; @@ -2275,8 +2318,6 @@ void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) { dfe->locked = true; ll_recursive_lock(dict, dfe->rw); - DICTIONARY_STATS_TRAVERSALS_PLUS1(dict); - // get the first item from the list DICTIONARY_ITEM *item = dict->items.list; @@ -3129,6 +3170,9 @@ struct thread_unittest { int join; DICTIONARY *dict; int dups; + + netdata_thread_t thread; + struct dictionary_stats stats; }; static void *unittest_dict_thread(void *arg) { @@ -3140,46 +3184,59 @@ static void *unittest_dict_thread(void *arg) { DICT_ITEM_CONST DICTIONARY_ITEM *item = dictionary_set_and_acquire_item_advanced(tu->dict, "dict thread checking 1234567890", -1, NULL, 0, NULL); - + tu->stats.ops.inserts++; dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + tu->stats.ops.searches++; void *t1; dfe_start_write(tu->dict, t1) { // this should delete the referenced item dictionary_del(tu->dict, t1_dfe.name); + tu->stats.ops.deletes++; void *t2; dfe_start_write(tu->dict, t2) { // this should add another dictionary_set(tu->dict, t2_dfe.name, NULL, 0); + tu->stats.ops.inserts++; dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + tu->stats.ops.searches++; // and this should delete it again dictionary_del(tu->dict, t2_dfe.name); + tu->stats.ops.deletes++; } dfe_done(t2); + tu->stats.ops.traversals++; // this should fail to add it dictionary_set(tu->dict, t1_dfe.name, NULL, 0); + tu->stats.ops.inserts++; + dictionary_del(tu->dict, t1_dfe.name); + tu->stats.ops.deletes++; } dfe_done(t1); + tu->stats.ops.traversals++; for(int i = 0; i < tu->dups ; i++) { dictionary_acquired_item_dup(tu->dict, item); dictionary_get(tu->dict, dictionary_acquired_item_name(item)); + tu->stats.ops.searches++; } for(int i = 0; i < tu->dups ; i++) { dictionary_acquired_item_release(tu->dict, item); dictionary_del(tu->dict, dictionary_acquired_item_name(item)); + tu->stats.ops.deletes++; } dictionary_acquired_item_release(tu->dict, item); dictionary_del(tu->dict, "dict thread checking 1234567890"); + tu->stats.ops.deletes++; // test concurrent deletions and flushes { @@ -3189,16 +3246,19 @@ static void *unittest_dict_thread(void *arg) { for (int i = 0; i < 1000; i++) { snprintfz(buf, 256, "del/flush test %d", i); dictionary_set(tu->dict, buf, NULL, 0); + tu->stats.ops.inserts++; } for (int i = 0; i < 1000; i++) { snprintfz(buf, 256, "del/flush test %d", i); dictionary_del(tu->dict, buf); + tu->stats.ops.deletes++; } } else { for (int i = 0; i < 10; i++) { dictionary_flush(tu->dict); + tu->stats.ops.flushes++; } } } @@ -3208,47 +3268,75 @@ static void *unittest_dict_thread(void *arg) { } static int dictionary_unittest_threads() { - - struct thread_unittest tu = { - .join = 0, - .dict = NULL, - .dups = 1, - }; - - // threads testing of dictionary - tu.dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE); time_t seconds_to_run = 5; int threads_to_create = 2; + + struct thread_unittest tu[threads_to_create]; + memset(tu, 0, sizeof(struct thread_unittest) * threads_to_create); + fprintf( - stderr, - "\nChecking dictionary concurrency with %d threads for %lld seconds...\n", - threads_to_create, - (long long)seconds_to_run); + stderr, + "\nChecking dictionary concurrency with %d threads for %lld seconds...\n", + threads_to_create, + (long long)seconds_to_run); + + // threads testing of dictionary + struct dictionary_stats stats = {}; + tu[0].join = 0; + tu[0].dups = 1; + tu[0].dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE, &stats, 0); - netdata_thread_t threads[threads_to_create]; - tu.join = 0; for (int i = 0; i < threads_to_create; i++) { + if(i) + tu[i] = tu[0]; + char buf[100 + 1]; snprintf(buf, 100, "dict%d", i); netdata_thread_create( - &threads[i], + &tu[i].thread, buf, NETDATA_THREAD_OPTION_DONT_LOG | NETDATA_THREAD_OPTION_JOINABLE, unittest_dict_thread, - &tu); + &tu[i]); } + sleep_usec(seconds_to_run * USEC_PER_SEC); - __atomic_store_n(&tu.join, 1, __ATOMIC_RELAXED); for (int i = 0; i < threads_to_create; i++) { + __atomic_store_n(&tu[i].join, 1, __ATOMIC_RELAXED); + void *retval; - netdata_thread_join(threads[i], &retval); + netdata_thread_join(tu[i].thread, &retval); + + if(i) { + tu[0].stats.ops.inserts += tu[i].stats.ops.inserts; + tu[0].stats.ops.deletes += tu[i].stats.ops.deletes; + tu[0].stats.ops.searches += tu[i].stats.ops.searches; + tu[0].stats.ops.flushes += tu[i].stats.ops.flushes; + tu[0].stats.ops.traversals += tu[i].stats.ops.traversals; + } } fprintf(stderr, - "inserts %zu" + "CALLS : inserts %zu" ", deletes %zu" ", searches %zu" + ", traversals %zu" + ", flushes %zu" + "\n", + tu[0].stats.ops.inserts, + tu[0].stats.ops.deletes, + tu[0].stats.ops.searches, + tu[0].stats.ops.traversals, + tu[0].stats.ops.flushes + ); + +#ifdef DICT_WITH_STATS + fprintf(stderr, + "ACTUAL: inserts %zu" + ", deletes %zu" + ", searches %zu" + ", traversals %zu" ", resets %zu" ", flushes %zu" ", entries %d" @@ -3259,22 +3347,23 @@ static int dictionary_unittest_threads() { ", delete spins %zu" ", search ignores %zu" "\n", - tu.dict->stats->ops.inserts, - tu.dict->stats->ops.deletes, - tu.dict->stats->ops.searches, - tu.dict->stats->ops.resets, - tu.dict->stats->ops.flushes, - tu.dict->entries, - tu.dict->referenced_items, - tu.dict->pending_deletion_items, - tu.dict->stats->spin_locks.use_spins, - tu.dict->stats->spin_locks.insert_spins, - tu.dict->stats->spin_locks.delete_spins, - tu.dict->stats->spin_locks.search_spins + stats.ops.inserts, + stats.ops.deletes, + stats.ops.searches, + stats.ops.traversals, + stats.ops.resets, + stats.ops.flushes, + tu[0].dict->entries, + tu[0].dict->referenced_items, + tu[0].dict->pending_deletion_items, + stats.spin_locks.use_spins, + stats.spin_locks.insert_spins, + stats.spin_locks.delete_spins, + stats.spin_locks.search_spins ); - dictionary_destroy(tu.dict); - tu.dict = NULL; +#endif + dictionary_destroy(tu[0].dict); return 0; } @@ -3407,6 +3496,7 @@ static int dictionary_unittest_view_threads() { netdata_thread_join(view_thread, &retval); netdata_thread_join(master_thread, &retval); +#ifdef DICT_WITH_STATS fprintf(stderr, "MASTER: inserts %zu" ", deletes %zu" @@ -3457,6 +3547,8 @@ static int dictionary_unittest_view_threads() { stats_view.spin_locks.delete_spins, stats_view.spin_locks.search_spins ); +#endif + dictionary_destroy(tv.master); dictionary_destroy(tv.view); diff --git a/libnetdata/dictionary/dictionary.h b/libnetdata/dictionary/dictionary.h index eea14d3fae..72efe1d038 100644 --- a/libnetdata/dictionary/dictionary.h +++ b/libnetdata/dictionary/dictionary.h @@ -35,6 +35,10 @@ * */ +#ifdef NETDATA_INTERNAL_CHECKS +#define DICT_WITH_STATS 1 +#endif + #ifdef DICTIONARY_INTERNALS #define DICTFE_CONST #define DICT_ITEM_CONST @@ -92,7 +96,7 @@ struct dictionary_stats { // memory struct { - long index; // bytes of keys indexed (indication of the index size) + long index; // bytes of keys indexed (indication of the index size) long values; // bytes of caller structures long dict; // bytes of the structures dictionary needs } memory; diff --git a/libnetdata/string/string.c b/libnetdata/string/string.c index 373d0c24c5..c76154e400 100644 --- a/libnetdata/string/string.c +++ b/libnetdata/string/string.c @@ -28,19 +28,22 @@ static struct string_partition { Pvoid_t JudyHSArray; // the Judy array - hashtable - size_t searches; // the number of successful searches in the index - size_t duplications; // when a string is referenced - size_t releases; // when a string is unreferenced - size_t inserts; // the number of successful inserts to the index size_t deletes; // the number of successful deleted from the index long int entries; // the number of entries in the index - long int active_references; // the number of active references alive long int memory; // the memory used, without the JudyHS index #ifdef NETDATA_INTERNAL_CHECKS // internal statistics + + struct { + size_t searches; // the number of successful searches in the index + size_t releases; // when a string is unreferenced + size_t duplications; // when a string is referenced + long int active_references; // the number of active references alive + } atomic; + size_t found_deleted_on_search; size_t found_available_on_search; size_t found_deleted_on_insert; @@ -51,14 +54,15 @@ static struct string_partition { } string_base[STRING_PARTITIONS] = { 0 }; #ifdef NETDATA_INTERNAL_CHECKS +#define string_stats_atomic_increment(partition, var) __atomic_add_fetch(&string_base[partition].atomic.var, 1, __ATOMIC_RELAXED) +#define string_stats_atomic_decrement(partition, var) __atomic_sub_fetch(&string_base[partition].atomic.var, 1, __ATOMIC_RELAXED) #define string_internal_stats_add(partition, var, val) __atomic_add_fetch(&string_base[partition].var, val, __ATOMIC_RELAXED) #else +#define string_stats_atomic_increment(partition, var) do {;} while(0) +#define string_stats_atomic_decrement(partition, var) do {;} while(0) #define string_internal_stats_add(partition, var, val) do {;} while(0) #endif -#define string_stats_atomic_increment(partition, var) __atomic_add_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED) -#define string_stats_atomic_decrement(partition, var) __atomic_sub_fetch(&string_base[partition].var, 1, __ATOMIC_RELAXED) - 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) { if (inserts) *inserts = 0; if (deletes) *deletes = 0; @@ -72,12 +76,15 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_ for(size_t i = 0; i < STRING_PARTITIONS ;i++) { if (inserts) *inserts += string_base[i].inserts; if (deletes) *deletes += string_base[i].deletes; - if (searches) *searches += string_base[i].searches; if (entries) *entries += (size_t) string_base[i].entries; - if (references) *references += (size_t) string_base[i].active_references; if (memory) *memory += (size_t) string_base[i].memory; - if (duplications) *duplications += string_base[i].duplications; - if (releases) *releases += string_base[i].releases; + +#ifdef NETDATA_INTERNAL_CHECKS + if (searches) *searches += string_base[i].atomic.searches; + if (references) *references += (size_t) string_base[i].atomic.active_references; + if (duplications) *duplications += string_base[i].atomic.duplications; + if (releases) *releases += string_base[i].atomic.releases; +#endif } } @@ -85,7 +92,9 @@ void string_statistics(size_t *inserts, size_t *deletes, size_t *searches, size_ #define string_entry_release(se) __atomic_sub_fetch(&((se)->refcount), 1, __ATOMIC_SEQ_CST); static inline bool string_entry_check_and_acquire(STRING *se) { +#ifdef NETDATA_INTERNAL_CHECKS uint8_t partition = string_partition(se); +#endif REFCOUNT expected, desired, count = 0; @@ -125,7 +134,9 @@ STRING *string_dup(STRING *string) { string_entry_acquire(string); +#ifdef NETDATA_INTERNAL_CHECKS uint8_t partition = string_partition(string); +#endif // statistics string_stats_atomic_increment(partition, active_references); @@ -275,7 +286,9 @@ static inline void string_index_delete(STRING *string) { STRING *string_strdupz(const char *str) { if(unlikely(!str || !*str)) return NULL; +#ifdef NETDATA_INTERNAL_CHECKS uint8_t partition = string_partition_str(str); +#endif size_t length = strlen(str) + 1; STRING *string = string_index_search(str, length); @@ -297,7 +310,9 @@ STRING *string_strdupz(const char *str) { void string_freez(STRING *string) { if(unlikely(!string)) return; +#ifdef NETDATA_INTERNAL_CHECKS uint8_t partition = string_partition(string); +#endif REFCOUNT refcount = string_entry_release(string); #ifdef NETDATA_INTERNAL_CHECKS |