diff options
author | paulfantom <pawel@krupa.net.pl> | 2018-09-09 15:23:28 +0200 |
---|---|---|
committer | paulfantom <pawel@krupa.net.pl> | 2018-09-09 15:25:53 +0200 |
commit | b862a0096b2e7f341c5b0e5dcb1a612913f68e55 (patch) | |
tree | 7bffd35d241739901518830feaff2be5b6f52b70 /tests/profile | |
parent | 5b1060413afabf7575c9c89b0610bd7f32872f28 (diff) |
:truck: move profiling to tests directory
Diffstat (limited to 'tests/profile')
-rw-r--r-- | tests/profile/Makefile | 12 | ||||
-rw-r--r-- | tests/profile/benchmark-dictionary.c | 129 | ||||
-rw-r--r-- | tests/profile/benchmark-line-parsing.c | 663 | ||||
-rw-r--r-- | tests/profile/benchmark-procfile-parser.c | 329 | ||||
-rwxr-xr-x | tests/profile/benchmark-registry.c | 227 | ||||
-rw-r--r-- | tests/profile/benchmark-strcmp.c | 663 | ||||
-rw-r--r-- | tests/profile/statsd-stress.c | 151 | ||||
-rw-r--r-- | tests/profile/test-eval.c | 299 |
8 files changed, 2473 insertions, 0 deletions
diff --git a/tests/profile/Makefile b/tests/profile/Makefile new file mode 100644 index 0000000000..16c6e43bc2 --- /dev/null +++ b/tests/profile/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-3.0+ + +COMMON_CFLAGS=-I ../../src/ -I ../../ +PROFILE_CFLAGS=-O1 -ggdb -Wall -Wextra + +benchmark-procfile-parser: benchmark-procfile-parser.c + gcc ${PROFILE_CFLAGS} ${COMMON_CFLAGS} -o $@ $^ ../../src/log.o ../../src/procfile.o ../../src/threads.o ../../src/locks.o ../../src/common.o ../../src/clocks.o ../../src/web_buffer.o ../../src/storage_number.o -pthread -lm + +statsd-stress: statsd-stress.c + gcc -O2 -Wall -Wextra -o $@ $^ -pthread + +all: statsd-stress benchmark-procfile-parser diff --git a/tests/profile/benchmark-dictionary.c b/tests/profile/benchmark-dictionary.c new file mode 100644 index 0000000000..ac115442a9 --- /dev/null +++ b/tests/profile/benchmark-dictionary.c @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-3.0+ */ +/* + * 1. build netdata (as normally) + * 2. cd tests/profile/ + * 3. compile with: + * gcc -O3 -Wall -Wextra -I ../../src/ -I ../../ -o benchmark-dictionary benchmark-dictionary.c ../../src/dictionary.o ../../src/log.o ../../src/avl.o ../../src/common.o -pthread + * + */ + +#include "common.h" + +struct myvalue { + int i; +}; + +void netdata_cleanup_and_exit(int ret) { exit(ret); } + +int main(int argc, char **argv) { + if(argc || argv) {;} + +// DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED|DICTIONARY_FLAG_WITH_STATISTICS); + DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_WITH_STATISTICS); + if(!dict) fatal("Cannot create dictionary."); + + struct rusage start, end; + unsigned long long dt; + char buf[100 + 1]; + struct myvalue value, *v; + int i, max = 30000000, max2; + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Inserting %d entries in the dictionary\n", max); + for(i = 0; i < max; i++) { + value.i = i; + snprintf(buf, 100, "%d", i); + + dictionary_set(dict, buf, &value, sizeof(struct myvalue)); + } + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Added %d entries in %llu nanoseconds: %llu inserts per second\n", max, dt, max * 1000000ULL / dt); + fprintf(stderr, " > Dictionary: %llu inserts, %llu deletes, %llu searches\n\n", dict->stats->inserts, dict->stats->deletes, dict->stats->searches); + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Retrieving %d entries from the dictionary\n", max); + for(i = 0; i < max; i++) { + value.i = i; + snprintf(buf, 100, "%d", i); + + v = dictionary_get(dict, buf); + if(!v) + fprintf(stderr, "ERROR: cannot get value %d from the dictionary\n", i); + else if(v->i != i) + fprintf(stderr, "ERROR: expected %d but got %d\n", i, v->i); + } + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Read %d entries in %llu nanoseconds: %llu searches per second\n", max, dt, max * 1000000ULL / dt); + fprintf(stderr, " > Dictionary: %llu inserts, %llu deletes, %llu searches\n\n", dict->stats->inserts, dict->stats->deletes, dict->stats->searches); + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Resetting %d entries in the dictionary\n", max); + for(i = 0; i < max; i++) { + value.i = i; + snprintf(buf, 100, "%d", i); + + dictionary_set(dict, buf, &value, sizeof(struct myvalue)); + } + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Reset %d entries in %llu nanoseconds: %llu resets per second\n", max, dt, max * 1000000ULL / dt); + fprintf(stderr, " > Dictionary: %llu inserts, %llu deletes, %llu searches\n\n", dict->stats->inserts, dict->stats->deletes, dict->stats->searches); + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Searching %d non-existing entries in the dictionary\n", max); + max2 = max * 2; + for(i = max; i < max2; i++) { + value.i = i; + snprintf(buf, 100, "%d", i); + + v = dictionary_get(dict, buf); + if(v) + fprintf(stderr, "ERROR: cannot got non-existing value %d from the dictionary\n", i); + } + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Searched %d non-existing entries in %llu nanoseconds: %llu not found searches per second\n", max, dt, max * 1000000ULL / dt); + fprintf(stderr, " > Dictionary: %llu inserts, %llu deletes, %llu searches\n\n", dict->stats->inserts, dict->stats->deletes, dict->stats->searches); + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Deleting %d entries from the dictionary\n", max); + for(i = 0; i < max; i++) { + value.i = i; + snprintf(buf, 100, "%d", i); + + dictionary_del(dict, buf); + } + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Deleted %d entries in %llu nanoseconds: %llu deletes per second\n", max, dt, max * 1000000ULL / dt); + fprintf(stderr, " > Dictionary: %llu inserts, %llu deletes, %llu searches\n\n", dict->stats->inserts, dict->stats->deletes, dict->stats->searches); + + // ------------------------------------------------------------------------ + + getrusage(RUSAGE_SELF, &start); + dict->stats->inserts = dict->stats->deletes = dict->stats->searches = 0ULL; + fprintf(stderr, "Destroying dictionary\n"); + dictionary_destroy(dict); + getrusage(RUSAGE_SELF, &end); + dt = (end.ru_utime.tv_sec * 1000000ULL + end.ru_utime.tv_usec) - (start.ru_utime.tv_sec * 1000000ULL + start.ru_utime.tv_usec); + fprintf(stderr, "Destroyed in %llu nanoseconds\n", dt); + + return 0; +} diff --git a/tests/profile/benchmark-line-parsing.c b/tests/profile/benchmark-line-parsing.c new file mode 100644 index 0000000000..d1cc0fde28 --- /dev/null +++ b/tests/profile/benchmark-line-parsing.c @@ -0,0 +1,663 @@ +/* SPDX-License-Identifier: GPL-3.0+ */ +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/time.h> + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define simple_hash(name) ({ \ + register unsigned char *__hash_source = (unsigned char *)(name); \ + register uint32_t __hash_value = 0x811c9dc5; \ + while (*__hash_source) { \ + __hash_value *= 16777619; \ + __hash_value ^= (uint32_t) *__hash_source++; \ + } \ + __hash_value; \ +}) + +static inline uint32_t simple_hash2(const char *name) { + register unsigned char *s = (unsigned char *)name; + register uint32_t hval = 0x811c9dc5; + while (*s) { + hval *= 16777619; + hval ^= (uint32_t) *s++; + } + return hval; +} + +static inline unsigned long long fast_strtoull(const char *s) { + register unsigned long long n = 0; + register char c; + for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + n *= 10; + n += c - '0'; + // n = (n << 1) + (n << 3) + (c - '0'); + } + return n; +} + +static uint32_t cache_hash = 0; +static uint32_t rss_hash = 0; +static uint32_t rss_huge_hash = 0; +static uint32_t mapped_file_hash = 0; +static uint32_t writeback_hash = 0; +static uint32_t dirty_hash = 0; +static uint32_t swap_hash = 0; +static uint32_t pgpgin_hash = 0; +static uint32_t pgpgout_hash = 0; +static uint32_t pgfault_hash = 0; +static uint32_t pgmajfault_hash = 0; +static uint32_t inactive_anon_hash = 0; +static uint32_t active_anon_hash = 0; +static uint32_t inactive_file_hash = 0; +static uint32_t active_file_hash = 0; +static uint32_t unevictable_hash = 0; +static uint32_t hierarchical_memory_limit_hash = 0; +static uint32_t total_cache_hash = 0; +static uint32_t total_rss_hash = 0; +static uint32_t total_rss_huge_hash = 0; +static uint32_t total_mapped_file_hash = 0; +static uint32_t total_writeback_hash = 0; +static uint32_t total_dirty_hash = 0; +static uint32_t total_swap_hash = 0; +static uint32_t total_pgpgin_hash = 0; +static uint32_t total_pgpgout_hash = 0; +static uint32_t total_pgfault_hash = 0; +static uint32_t total_pgmajfault_hash = 0; +static uint32_t total_inactive_anon_hash = 0; +static uint32_t total_active_anon_hash = 0; +static uint32_t total_inactive_file_hash = 0; +static uint32_t total_active_file_hash = 0; +static uint32_t total_unevictable_hash = 0; + +char *strings[] = { + "cache", + "rss", + "rss_huge", + "mapped_file", + "writeback", + "dirty", + "swap", + "pgpgin", + "pgpgout", + "pgfault", + "pgmajfault", + "inactive_anon", + "active_anon", + "inactive_file", + "active_file", + "unevictable", + "hierarchical_memory_limit", + "total_cache", + "total_rss", + "total_rss_huge", + "total_mapped_file", + "total_writeback", + "total_dirty", + "total_swap", + "total_pgpgin", + "total_pgpgout", + "total_pgfault", + "total_pgmajfault", + "total_inactive_anon", + "total_active_anon", + "total_inactive_file", + "total_active_file", + "total_unevictable", + NULL +}; + +unsigned long long values1[12] = { 0 }; +unsigned long long values2[12] = { 0 }; +unsigned long long values3[12] = { 0 }; +unsigned long long values4[12] = { 0 }; +unsigned long long values5[12] = { 0 }; +unsigned long long values6[12] = { 0 }; + +#define NUMBER1 "12345678901234" +#define NUMBER2 "23456789012345" +#define NUMBER3 "34567890123456" +#define NUMBER4 "45678901234567" +#define NUMBER5 "56789012345678" +#define NUMBER6 "67890123456789" +#define NUMBER7 "78901234567890" +#define NUMBER8 "89012345678901" +#define NUMBER9 "90123456789012" +#define NUMBER10 "12345678901234" +#define NUMBER11 "23456789012345" + +// simple system strcmp() +void test1() { + int i; + for(i = 0; strings[i] ; i++) { + char *s = strings[i]; + + if(unlikely(!strcmp(s, "cache"))) + values1[i] = strtoull(NUMBER1, NULL, 10); + + else if(unlikely(!strcmp(s, "rss"))) + values1[i] = strtoull(NUMBER2, NULL, 10); + + else if(unlikely(!strcmp(s, "rss_huge"))) + values1[i] = strtoull(NUMBER3, NULL, 10); + + else if(unlikely(!strcmp(s, "mapped_file"))) + values1[i] = strtoull(NUMBER4, NULL, 10); + + else if(unlikely(!strcmp(s, "writeback"))) + values1[i] = strtoull(NUMBER5, NULL, 10); + + else if(unlikely(!strcmp(s, "dirty"))) + values1[i] = strtoull(NUMBER6, NULL, 10); + + else if(unlikely(!strcmp(s, "swap"))) + values1[i] = strtoull(NUMBER7, NULL, 10); + + else if(unlikely(!strcmp(s, "pgpgin"))) + values1[i] = strtoull(NUMBER8, NULL, 10); + + else if(unlikely(!strcmp(s, "pgpgout"))) + values1[i] = strtoull(NUMBER9, NULL, 10); + + else if(unlikely(!strcmp(s, "pgfault"))) + values1[i] = strtoull(NUMBER10, NULL, 10); + + else if(unlikely(!strcmp(s, "pgmajfault"))) + values1[i] = strtoull(NUMBER11, NULL, 10); + } +} + +// inline simple_hash() with system strtoull() +void test2() { + int i; + for(i = 0; strings[i] ; i++) { + char *s = strings[i]; + uint32_t hash = simple_hash2(s); + + if(unlikely(hash == cache_hash && !strcmp(s, "cache"))) + values2[i] = strtoull(NUMBER1, NULL, 10); + + else if(unlikely(hash == rss_hash && !strcmp(s, "rss"))) + values2[i] = strtoull(NUMBER2, NULL, 10); + + else if(unlikely(hash == rss_huge_hash && !strcmp(s, "rss_huge"))) + values2[i] = strtoull(NUMBER3, NULL, 10); + + else if(unlikely(hash == mapped_file_hash && !strcmp(s, "mapped_file"))) + values2[i] = strtoull(NUMBER4, NULL, 10); + + else if(unlikely(hash == writeback_hash && !strcmp(s, "writeback"))) + values2[i] = strtoull(NUMBER5, NULL, 10); + + else if(unlikely(hash == dirty_hash && !strcmp(s, "dirty"))) + values2[i] = strtoull(NUMBER6, NULL, 10); + + else if(unlikely(hash == swap_hash && !strcmp(s, "swap"))) + values2[i] = strtoull(NUMBER7, NULL, 10); + + else if(unlikely(hash == pgpgin_hash && !strcmp(s, "pgpgin"))) + values2[i] = strtoull(NUMBER8, NULL, 10); + + else if(unlikely(hash == pgpgout_hash && !strcmp(s, "pgpgout"))) + values2[i] = strtoull(NUMBER9, NULL, 10); + + else if(unlikely(hash == pgfault_hash && !strcmp(s, "pgfault"))) + values2[i] = strtoull(NUMBER10, NULL, 10); + + else if(unlikely(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))) + values2[i] = strtoull(NUMBER11, NULL, 10); + } +} + +// statement expression simple_hash(), system strtoull() +void test3() { + int i; + for(i = 0; strings[i] ; i++) { + char *s = strings[i]; + uint32_t hash = simple_hash(s); + + if(unlikely(hash == cache_hash && !strcmp(s, "cache"))) + values3[i] = strtoull(NUMBER1, NULL, 10); + + else if(unlikely(hash == rss_hash && !strcmp(s, "rss"))) + values3[i] = strtoull(NUMBER2, NULL, 10); + + else if(unlikely(hash == rss_huge_hash && !strcmp(s, "rss_huge"))) + values3[i] = strtoull(NUMBER3, NULL, 10); + + else if(unlikely(hash == mapped_file_hash && !strcmp(s, "mapped_file"))) + values3[i] = strtoull(NUMBER4, NULL, 10); + + else if(unlikely(hash == writeback_hash && !strcmp(s, "writeback"))) + values3[i] = strtoull(NUMBER5, NULL, 10); + + else if(unlikely(hash == dirty_hash && !strcmp(s, "dirty"))) + values3[i] = strtoull(NUMBER6, NULL, 10); + + else if(unlikely(hash == swap_hash && !strcmp(s, "swap"))) + values3[i] = strtoull(NUMBER7, NULL, 10); + + else if(unlikely(hash == pgpgin_hash && !strcmp(s, "pgpgin"))) + values3[i] = strtoull(NUMBER8, NULL, 10); + + else if(unlikely(hash == pgpgout_hash && !strcmp(s, "pgpgout"))) + values3[i] = strtoull(NUMBER9, NULL, 10); + + else if(unlikely(hash == pgfault_hash && !strcmp(s, "pgfault"))) + values3[i] = strtoull(NUMBER10, NULL, 10); + + else if(unlikely(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))) + values3[i] = strtoull(NUMBER11, NULL, 10); + } +} + + +// inline simple_hash(), if-continue checks +void test4() { + int i; + for(i = 0; strings[i] ; i++) { + char *s = strings[i]; + uint32_t hash = simple_hash2(s); + + if(unlikely(hash == cache_hash && !strcmp(s, "cache"))) { + values4[i] = strtoull(NUMBER1, NULL, 0); + continue; + } + + if(unlikely(hash == rss_hash && !strcmp(s, "rss"))) { + values4[i] = strtoull(NUMBER2, NULL, 0); + continue; + } + + if(unlikely(hash == rss_huge_hash && !strcmp(s, "rss_huge"))) { + values4[i] = strtoull(NUMBER3, NULL, 0); + continue; + } + + if(unlikely(hash == mapped_file_hash && !strcmp(s, "mapped_file"))) { + values4[i] = strtoull(NUMBER4, NULL, 0); + continue; + } + + if(unlikely(hash == writeback_hash && !strcmp(s, "writeback"))) { + values4[i] = strtoull(NUMBER5, NULL, 0); + continue; + } + + if(unlikely(hash == dirty_hash && !strcmp(s, "dirty"))) { + values4[i] = strtoull(NUMBER6, NULL, 0); + continue; + } + + if(unlikely(hash == swap_hash && !strcmp(s, "swap"))) { + values4[i] = strtoull(NUMBER7, NULL, 0); + continue; + } + + if(unlikely(hash == pgpgin_hash && !strcmp(s, "pgpgin"))) { + values4[i] = strtoull(NUMBER8, NULL, 0); + continue; + } + + if(unlikely(hash == pgpgout_hash && !strcmp(s, "pgpgout"))) { + values4[i] = strtoull(NUMBER9, NULL, 0); + continue; + } + + if(unlikely(hash == pgfault_hash && !strcmp(s, "pgfault"))) { + values4[i] = strtoull(NUMBER10, NULL, 0); + continue; + } + + if(unlikely(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))) { + values4[i] = strtoull(NUMBER11, NULL, 0); + continue; + } + } +} + +// inline simple_hash(), if-else-if-else-if (netdata default) +void test5() { + int i; + for(i = 0; strings[i] ; i++) { + char *s = strings[i]; + uint32_t hash = simple_hash2(s); + + if(unlikely(hash == cache_hash && !strcmp(s, "cache"))) + values5[i] = fast_strtoull(NUMBER1); + + else if(unlikely(hash == rss_hash && !strcmp(s, "rss"))) + values5[i] = fast_strtoull(NUMBER2); + + else if(unlikely(hash == rss_huge_hash && !strcmp(s, "rss_huge"))) + values5[i] = fast_strtoull(NUMBER3); + + else if(unlikely(hash == mapped_file_hash && !strcmp(s, "mapped_file"))) + values5[i] = fast_strtoull(NUMBER4); + + else if(unlikely(hash == writeback_hash && !strcmp(s, "writeback"))) + values5[i] = fast_strtoull(NUMBER5); + + else if(unlikely(hash == dirty_hash && !strcmp(s, "dirty"))) + values5[i] = fast_strtoull(NUMBER6); + + else if(unlikely(hash == swap_hash && !strcmp(s, "swap"))) + values5[i] = fast_strtoull(NUMBER7); + + else if(unlikely(hash == pgpgin_hash && !strcmp(s, "pgpgin"))) + values5[i] = fast_strtoull(NUMBER8); + + else if(unlikely(hash == pgpgout_hash && !strcmp(s, "pgpgout"))) + values5[i] = fast_strtoull(NUMBER9); + + else if(unlikely(hash == pgfault_hash && !strcmp(s, "pgfault"))) + values5[i] = fast_strtoull(NUMBER10); + + else if(unlikely(hash == pgmajfault_hash && !strcmp(s, "pgmajfault"))) + values5[i] = fast_strtoull(NUMBER11); + } +} + +// ---------------------------------------------------------------------------- + +struct entry { + char *name; + uint32_t hash; + int found; + void (*func)(void *data1, void *data2); + void *data1; + void *data2; + struct entry *prev, *next; +}; + +struct base { + int iteration; + int registered; + int wanted; + int found; + struct entry *entries, *last; +}; + +static inline void callback(void *data1, void *data2) { + char *string = data1; + unsigned long long *value = data2; + *value = fast_strtoull(string); +} + + +static inline struct base *entry(struct base *base, const char *name, void *data1, void *data2, void (*func)(void *, void *)) { + if(!base) + base = calloc(1, sizeof(struct base)); + + struct entry *e = malloc(sizeof(struct entry)); + e->name = strdup(name); + e->hash = simple_hash2(e->name); + e->data1 = data1; + e->data2 = data2; + e->func = func; + e->prev = NULL; + e->next = base->entries; + + if(base->entries) base->entries->prev = e; + else base->last = e; + + base->entries = e; + base->registered++; + base->wanted = base->registered; + + return base; +} + +static inline int check(struct base *base, const char *s) { + uint32_t hash = simple_hash2(s); + + if(likely(hash == base->last->hash && !strcmp(s, base->last->name))) { + base->last->found = 1; + base->found++; + if(base->last->func) base->last->func(base->last->data1, base->last->data2); + base->last = base->last->next; + + if(!base->last) + base->last = base->entries; + + if(base->found == base->registered) + return 1; + + return 0; + } + + // find it + struct entry *e; + for(e = base->entries; e ; e = e->next) + if(e->hash == hash && !strcmp(e->name, s)) + break; + + if(e == base->last) { + printf("ERROR\n"); + exit(1); + } + + if(e) { + // found + + // run it + if(e->func) e->func(e->data1, e->data2); + + // unlink it + if(e->next) e->next->prev = e->prev; + if(e->prev) e->prev->next = e->next; + + if(base->entries == e) + base->entries = e->next; + } + else { + // not found + + // create it + e = calloc(1, sizeof(struct entry)); + e->name = strdup(s); + e->hash = hash; + } + + // link it here + e->next = base->last; + if(base->last) { + e->prev = base->last->prev; + base->last->prev = e; + + if(base->entries == base->last) + base->entries = e; + } + else + e->prev = NULL; + + if(e->prev) + e->prev->next = e; + + base->last = e->next; + if(!base->last) + base->last = base->entries; + + e->found = 1; + base->found++; + + if(base->found == base->registered) + return 1; + + printf("relinked '%s' after '%s' and before '%s': ", e->name, e->prev?e->prev->name:"NONE", e->next?e->next->name:"NONE"); + for(e = base->entries; e ; e = e->next) printf("%s ", e->name); + printf("\n"); + + return 0; +} + +static inline void begin(struct base *base) { + + if(unlikely(base->iteration % 60) == 1) { + base->wanted = 0; + struct entry *e; + for(e = base->entries; e ; e = e->next) + if(e->found) base->wanted++; + } + + base->iteration++; + base->last = base->entries; + base->found = 0; +} + +void test6() { + + static struct base *base = NULL; + + if(unlikely(!base)) { + base = entry(base, "cache", NUMBER1, &values6[0], callback); + base = entry(base, "rss", NUMBER2, &values6[1], callback); + base = entry(base, "rss_huge", NUMBER3, &values6[2], callback); + base = entry(base, "mapped_file", NUMBER4, &values6[3], callback); + base = entry(base, "writeback", NUMBER5, &values6[4], callback); + base = entry(base, "dirty", NUMBER6, &values6[5], callback); + base = entry(base, "swap", NUMBER7, &values6[6], callback); + base = entry(base, "pgpgin", NUMBER8, &values6[7], callback); + base = entry(base, "pgpgout", NUMBER9, &values6[8], callback); + base = entry(base, "pgfault", NUMBER10, &values6[9], callback); + base = entry(base, "pgmajfault", NUMBER11, &values6[10], callback); + } + + begin(base); + + int i; + for(i = 0; strings[i] ; i++) { + if(check(base, strings[i])) + break; + } +} + +// ---------------------------------------------------------------------------- + + +// ============== +// --- Poor man cycle counting. +static unsigned long tsc; + +static void begin_tsc(void) +{ + unsigned long a, d; + asm volatile ("cpuid\nrdtsc" : "=a" (a), "=d" (d) : "0" (0) : "ebx", "ecx"); + tsc = ((unsigned long)d << 32) | (unsigned long)a; +} + +static unsigned long end_tsc(void) +{ + unsigned long a, d; + asm volatile ("rdtscp" : "=a" (a), "=d" (d) : : "ecx"); + return (((unsigned long)d << 32) | (unsigned long)a) - tsc; +} +// =============== + +static unsigned long long clk; + +static void begin_clock() { + struct timeval tv; + if(unlikely(gettimeofday(&tv, NULL) == -1)) + return; + clk = tv.tv_sec * 1000000 + tv.tv_usec; +} + +static unsigned long long end_clock() { + struct timeval tv; + if(unlikely(gettimeofday(&tv, NULL) == -1)) + return -1; + return clk = tv.tv_sec * 1000000 + tv.tv_usec - clk; +} + +void main(void) +{ + cache_hash = simple_hash("cache"); + rss_hash = simple_hash("rss"); + rss_huge_hash = simple_hash("rss_huge"); + mapped_file_hash = simple_hash("mapped_file"); + writeback_hash = simple_hash("writeback"); + dirty_hash = simple_hash("dirty"); + swap_hash = simple_hash("swap"); + pgpgin_hash = simple_hash("pgpgin"); + pgpgout_hash = simple_hash("pgpgout"); + pgfault_hash = simple_hash("pgfault"); + pgmajfault_hash = simple_hash("pgmajfault"); + inactive_anon_hash = simple_hash("inactive_anon"); + active_anon_hash = simple_hash("active_anon"); + inactive_file_hash = simple_hash("inactive_file"); + active_file_hash = simple_hash("active_file"); + unevictable_hash = simple_hash("unevictable"); + hierarchical_memory_limit_hash = simple_hash("hierarchical_memory_limit"); + total_cache_hash = simple_hash("total_cache"); + total_rss_hash = simple_hash("total_rss"); + total_rss_huge_hash = simple_hash("total_rss_huge"); + total_mapped_file_hash = simple_hash("total_mapped_file"); + total_writeback_hash = simple_hash("total_writeback"); + total_dirty_hash = simple_hash("total_dirty"); + total_swap_hash = simple_hash("total_swap"); + total_pgpgin_hash = simple_hash("total_pgpgin"); + total_pgpgout_hash = simple_hash("total_pgpgout"); + total_pgfault_hash = simple_hash("total_pgfault"); + total_pgmajfault_hash = simple_hash("total_pgmajfault"); + total_inactive_anon_hash = simple_hash("total_inactive_anon"); + total_active_anon_hash = simple_hash("total_active_anon"); + total_inactive_file_hash = simple_hash("total_inactive_file"); + total_active_file_hash = simple_hash("total_active_file"); + total_unevictable_hash = simple_hash("total_unevictable"); + + // cache functions + (void)simple_hash2("hello world"); + (void)strcmp("1", "2"); + (void)strtoull("123", NULL, 0); + + unsigned long i, c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0, c6 = 0; + unsigned long max = 200000; + + begin_clock(); + for(i = 0; i <= max ;i++) test1(); + c1 = end_clock(); + + begin_clock(); + for(i = 0; i <= max ;i++) test2(); + c2 = end_clock(); + + begin_clock(); + for(i = 0; i <= max ;i++) test3(); + c3 = end_clock(); + + begin_clock(); + for(i = 0; i <= max ;i++) test4(); + c4 = end_clock(); + + begin_clock(); + for(i = 0; i <= max ;i++) test5(); + c5 = end_clock(); + + begin_clock(); + for(i = 0; i <= max ;i++) test6(); + c6 = end_clock(); + + for(i = 0; i < 11 ; i++) + printf("value %lu: %llu %llu %llu %llu %llu %llu\n", i, values1[i], values2[i], values3[i], values4[i], values5[i], values6[i]); + + printf("\n\nRESULTS\n"); + printf("test1() in %lu usecs: simple system strcmp().\n" + "test2() in %lu usecs: inline simple_hash() with system strtoull().\n" + "test3() in %lu usecs: statement expression simple_hash(), system strtoull().\n" + "test4() in %lu usecs: inline simple_hash(), if-continue checks.\n" + "test5() in %lu usecs: inline simple_hash(), if-else-if-else-if (netdata default).\n" + "test6() in %lu usecs: adaptive re-sortable array (wow!)\n" + , c1 + , c2 + , c3 + , c4 + , c5 + , c6 + ); + +} diff --git a/tests/profile/benchmark-procfile-parser.c b/tests/profile/benchmark-procfile-parser.c new file mode 100644 index 0000000000..2ec23265dd --- /dev/null +++ b/tests/profile/benchmark-procfile-parser.c @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-3.0+ */ +#include "config.h" +#include "common.h" +#include "clocks.h" + +void netdata_cleanup_and_exit(int ret) { + exit(ret); +} + +#define PF_PREFIX "PROCFILE" +#define PFWORDS_INCREASE_STEP 200 +#define PFLINES_INCREASE_STEP 10 +#define PROCFILE_INCREMENT_BUFFER 512 +extern size_t procfile_max_lines; +extern size_t procfile_max_words; +extern size_t procfile_max_allocation; + + +static inline void pflines_reset(pflines *fl) { + // debug(D_PROCFILE, PF_PREFIX ": reseting lines"); + + fl->len = 0; +} + +static inline void pflines_free(pflines *fl) { + // debug(D_PROCFILE, PF_PREFIX ": freeing lines"); + + freez(fl); +} + +static inline void pfwords_reset(pfwords *fw) { + // debug(D_PROCFILE, PF_PREFIX ": reseting words"); + fw->len = 0; +} + + +static inline void pfwords_add(procfile *ff, char *str) { + // debug(D_PROCFILE, PF_PREFIX ": adding word No %d: '%s'", fw->len, str); + + pfwords *fw = ff->words; + if(unlikely(fw->len == fw->size)) { + // debug(D_PROCFILE, PF_PREFIX ": expanding words"); + + ff->words = fw = reallocz(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *)); + fw->size += PFWORDS_INCREASE_STEP; + } + + fw->words[fw->len++] = str; +} + +NEVERNULL +static inline size_t *pflines_add(procfile *ff) { + // debug(D_PROCFILE, PF_PREFIX ": adding line %d at word %d", fl->len, first_word); + + pflines *fl = ff->lines; + if(unlikely(fl->len == fl->size)) { + // debug(D_PROCFILE, PF_PREFIX ": expanding lines"); + + ff->lines = fl = reallocz(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline)); + fl->size += PFLINES_INCREASE_STEP; + } + + ffline *ffl = &fl->lines[fl->len++]; + ffl->words = 0; + ffl->first = ff->words->len; + + return &ffl->words; +} + + +NOINLINE +static void procfile_parser(procfile *ff) { + // debug(D_PROCFILE, PF_PREFIX ": Parsing file '%s'", ff->filename); + + char *s = ff->data // our current position + , *e = &ff->data[ff->len] // the terminating null + , *t = ff->data; // the first character of a word (or quoted / parenthesized string) + + // the look up array to find our type of character + PF_CHAR_TYPE *separators = ff->separators; + + char quote = 0; // the quote character - only when in quoted string + size_t opened = 0; // counts the number of open parenthesis + + size_t *line_words = pflines_add(ff); + + while(s < e) { + PF_CHAR_TYPE ct = separators[(unsigned char)(*s)]; + + // this is faster than a switch() + // read more here: http://lazarenko.me/switch/ + switch(ct) { + case PF_CHAR_IS_SEPARATOR: + if(!quote && !opened) { + if (s != t) { + // separator, but we have word before it + *s = '\0'; + pfwords_add(ff, t); + (*line_words)++; + } + t = s + 1; + } + // fallthrough + + case PF_CHAR_IS_WORD: + s++; + break; + + + case PF_CHAR_IS_NEWLINE: + // end of line + + *s = '\0'; + pfwords_add(ff, t); + (*line_words)++; + t = ++s; + + // debug(D_PROCFILE, PF_PREFIX ": ended line %d with %d words", l, ff->lines->lines[l].words); + + line_words = pflines_add(ff); + break; + |