summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-09-01 15:38:09 +0300
committerGitHub <noreply@github.com>2023-09-01 15:38:09 +0300
commit6ee8ea23f32be1ce0e373f0c3822e415edd2db03 (patch)
treebaeb745e3debf1858494d5688968fc6a9530916f
parent24006ed5c19cf7c398cf8a76e7bdce1f60c7e5a4 (diff)
Limit atomic operations for statistics (#15887)
Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>
-rw-r--r--daemon/global_statistics.c31
-rw-r--r--libnetdata/dictionary/dictionary.c212
-rw-r--r--libnetdata/dictionary/dictionary.h6
-rw-r--r--libnetdata/string/string.c39
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