#include <assert.h>
#include <stdlib.h>
+#include <isc/barrier.h>
#include <isc/file.h>
#include <isc/hashmap.h>
#include <isc/ht.h>
#include <isc/list.h>
#include <isc/rwlock.h>
+#include <isc/thread.h>
#include <isc/urcu.h>
#include <isc/util.h>
#include <tests/dns.h>
#include <tests/qp.h>
-struct {
+struct item_s {
const char *text;
dns_fixedname_t fixed;
+ struct cds_lfht_node ht_node;
} item[1024 * 1024];
+isc_barrier_t barrier;
+isc_rwlock_t rwl;
+
+struct thread_s {
+ isc_thread_t thread;
+ struct fun *fun;
+ void *map;
+ size_t start;
+ size_t end;
+ uint64_t d0;
+ uint64_t d1;
+} threads[1024];
+
static void
item_check(void *ctx, void *pval, uint32_t ival) {
UNUSED(ctx);
testname,
};
+#define CHECK(count, result) \
+ do { \
+ if (result != ISC_R_SUCCESS) { \
+ dns_name_t *name = &item[count].fixed.name; \
+ char buf[DNS_NAME_MAXTEXT] = { 0 }; \
+ dns_name_format(name, buf, sizeof(buf)); \
+ fprintf(stderr, "%s: %s\n", buf, \
+ isc_result_totext(result)); \
+ exit(1); \
+ } \
+ } while (0)
+
+struct fun {
+ const char *name;
+ void *(*new)(isc_mem_t *mem);
+ isc_threadfunc_t thread;
+};
+
+/*
+ * cds_lfht
+ */
+
+static void *
+new_lfht(isc_mem_t *mem ISC_ATTR_UNUSED) {
+ struct cds_lfht *lfht = cds_lfht_new(
+ 1, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
+ return (lfht);
+}
+
+static int
+lfht_match(struct cds_lfht_node *ht_node, const void *_key) {
+ const struct item_s *i = caa_container_of(ht_node, struct item_s,
+ ht_node);
+ const dns_name_t *key = _key;
+
+ return (dns_name_equal(key, &i->fixed.name));
+}
+
+static isc_result_t
+add_lfht(void *lfht, size_t count) {
+ unsigned long hash = dns_name_hash(&item[count].fixed.name);
+
+ struct cds_lfht_node *ht_node = cds_lfht_add_unique(
+ lfht, hash, lfht_match, &item[count].fixed.name,
+ &item[count].ht_node);
+
+ if (ht_node != &item[count].ht_node) {
+ return (ISC_R_EXISTS);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+get_lfht(void *lfht, size_t count, void **pval) {
+ unsigned long hash = dns_name_hash(&item[count].fixed.name);
+
+ struct cds_lfht_iter iter;
+ cds_lfht_lookup(lfht, hash, lfht_match, &item[count].fixed.name, &iter);
+
+ struct cds_lfht_node *ht_node = cds_lfht_iter_get_node(&iter);
+ if (ht_node == NULL) {
+ return (ISC_R_NOTFOUND);
+ }
+
+ *pval = caa_container_of(ht_node, struct item_s, ht_node);
+ return (ISC_R_SUCCESS);
+}
+
+static void *
+thread_lfht(void *arg0) {
+ struct thread_s *arg = arg0;
+
+ isc_barrier_wait(&barrier);
+
+ isc_time_t t0 = isc_time_now_hires();
+ for (size_t n = arg->start; n < arg->end; n++) {
+ isc_result_t result = add_lfht(arg->map, n);
+ CHECK(n, result);
+ }
+
+ isc_time_t t1 = isc_time_now_hires();
+ for (size_t n = arg->start; n < arg->end; n++) {
+ void *pval = NULL;
+ isc_result_t result = get_lfht(arg->map, n, &pval);
+ CHECK(n, result);
+ assert(pval == &item[n]);
+ }
+
+ isc_time_t t2 = isc_time_now_hires();
+
+ arg->d0 = isc_time_microdiff(&t1, &t0);
+ arg->d1 = isc_time_microdiff(&t2, &t1);
+
+ return (NULL);
+}
+
/*
* hashmap
*/
static void *
new_hashmap(isc_mem_t *mem) {
isc_hashmap_t *hashmap = NULL;
- isc_hashmap_create(mem, 16, 0, &hashmap);
+ isc_hashmap_create(mem, 1, 0, &hashmap);
+
return (hashmap);
}
static isc_result_t
add_hashmap(void *hashmap, size_t count) {
- return (isc_hashmap_add(hashmap, NULL, item[count].fixed.name.ndata,
- item[count].fixed.name.length, &item[count]));
-}
-
-static void
-sqz_hashmap(void *hashmap) {
- UNUSED(hashmap);
+ isc_result_t result =
+ isc_hashmap_add(hashmap, NULL, item[count].fixed.name.ndata,
+ item[count].fixed.name.length, &item[count]);
+ return (result);
}
static isc_result_t
get_hashmap(void *hashmap, size_t count, void **pval) {
- return (isc_hashmap_find(hashmap, NULL, item[count].fixed.name.ndata,
- item[count].fixed.name.length, pval));
+ isc_result_t result =
+ isc_hashmap_find(hashmap, NULL, item[count].fixed.name.ndata,
+ item[count].fixed.name.length, pval);
+ return (result);
+}
+
+static void *
+thread_hashmap(void *arg0) {
+ struct thread_s *arg = arg0;
+
+ isc_barrier_wait(&barrier);
+
+ isc_time_t t0 = isc_time_now_hires();
+ WRLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ isc_result_t result = add_hashmap(arg->map, n);
+ CHECK(n, result);
+ }
+ WRUNLOCK(&rwl);
+
+ isc_time_t t1 = isc_time_now_hires();
+ RDLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ void *pval = NULL;
+ isc_result_t result = get_hashmap(arg->map, n, &pval);
+ CHECK(n, result);
+ assert(pval == &item[n]);
+ }
+ RDUNLOCK(&rwl);
+ isc_time_t t2 = isc_time_now_hires();
+
+ arg->d0 = isc_time_microdiff(&t1, &t0);
+ arg->d1 = isc_time_microdiff(&t2, &t1);
+
+ return (NULL);
}
/*
static void *
new_ht(isc_mem_t *mem) {
isc_ht_t *ht = NULL;
- isc_ht_init(&ht, mem, 16, 0);
+ isc_ht_init(&ht, mem, 1, 0);
return (ht);
}
static isc_result_t
add_ht(void *ht, size_t count) {
- return (isc_ht_add(ht, item[count].fixed.name.ndata,
- item[count].fixed.name.length, &item[count]));
-}
-
-static void
-sqz_ht(void *ht) {
- UNUSED(ht);
+ isc_result_t result = isc_ht_add(ht, item[count].fixed.name.ndata,
+ item[count].fixed.name.length,
+ &item[count]);
+ return (result);
}
static isc_result_t
get_ht(void *ht, size_t count, void **pval) {
- return (isc_ht_find(ht, item[count].fixed.name.ndata,
- item[count].fixed.name.length, pval));
+ isc_result_t result = isc_ht_find(ht, item[count].fixed.name.ndata,
+ item[count].fixed.name.length, pval);
+ return (result);
+}
+
+static void *
+thread_ht(void *arg0) {
+ struct thread_s *arg = arg0;
+
+ isc_barrier_wait(&barrier);
+
+ isc_time_t t0 = isc_time_now_hires();
+ WRLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ isc_result_t result = add_ht(arg->map, n);
+ CHECK(n, result);
+ }
+ WRUNLOCK(&rwl);
+
+ isc_time_t t1 = isc_time_now_hires();
+ RDLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ void *pval = NULL;
+ isc_result_t result = get_ht(arg->map, n, &pval);
+ CHECK(n, result);
+ assert(pval == &item[n]);
+ }
+ RDUNLOCK(&rwl);
+ isc_time_t t2 = isc_time_now_hires();
+
+ arg->d0 = isc_time_microdiff(&t1, &t0);
+ arg->d1 = isc_time_microdiff(&t2, &t1);
+
+ return (NULL);
}
/*
static isc_result_t
add_rbt(void *rbt, size_t count) {
- return (dns_rbt_addname(rbt, &item[count].fixed.name, &item[count]));
-}
-
-static void
-sqz_rbt(void *rbt) {
- UNUSED(rbt);
+ isc_result_t result = dns_rbt_addname(rbt, &item[count].fixed.name,
+ &item[count]);
+ return (result);
}
static isc_result_t
get_rbt(void *rbt, size_t count, void **pval) {
- return (dns_rbt_findname(rbt, &item[count].fixed.name, 0, NULL, pval));
+ isc_result_t result = dns_rbt_findname(rbt, &item[count].fixed.name, 0,
+ NULL, pval);
+ return (result);
+}
+
+static void *
+thread_rbt(void *arg0) {
+ struct thread_s *arg = arg0;
+
+ isc_barrier_wait(&barrier);
+
+ isc_time_t t0 = isc_time_now_hires();
+ WRLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ isc_result_t result = add_rbt(arg->map, n);
+ CHECK(n, result);
+ }
+ WRUNLOCK(&rwl);
+
+ isc_time_t t1 = isc_time_now_hires();
+ RDLOCK(&rwl);
+ for (size_t n = arg->start; n < arg->end; n++) {
+ void *pval = NULL;
+ isc_result_t result = get_rbt(arg->map, n, &pval);
+ CHECK(n, result);
+ assert(pval == &item[n]);
+ }
+ RDUNLOCK(&rwl);
+
+ isc_time_t t2 = isc_time_now_hires();
+
+ arg->d0 = isc_time_microdiff(&t1, &t0);
+ arg->d1 = isc_time_microdiff(&t2, &t1);
+
+ return (NULL);
}
/*
static void *
new_qp(isc_mem_t *mem) {
- dns_qp_t *qp = NULL;
- dns_qp_create(mem, &qpmethods, NULL, &qp);
- return (qp);
+ dns_qpmulti_t *qpmulti = NULL;
+ dns_qpmulti_create(mem, &qpmethods, NULL, &qpmulti);
+ return (qpmulti);
}
static isc_result_t
add_qp(void *qp, size_t count) {
- return (dns_qp_insert(qp, &item[count], count));
+ isc_result_t result = dns_qp_insert(qp, &item[count], count);
+ return (result);
}
static void
static isc_result_t
get_qp(void *qp, size_t count, void **pval) {
uint32_t ival = 0;
- return (dns_qp_getname(qp, &item[count].fixed.name, pval, &ival));
+ isc_result_t result = dns_qp_getname(qp, &item[count].fixed.name, pval,
+ &ival);
+ return (result);
+}
+
+static void *
+_thread_qp(void *arg0, bool sqz, bool brr) {
+ struct thread_s *arg = arg0;
+
+ isc_barrier_wait(&barrier);
+
+ dns_qp_t *qp = NULL;
+ dns_qpmulti_write(arg->map, &qp);
+
+ isc_time_t t0 = isc_time_now_hires();
+ for (size_t n = arg->start; n < arg->end; n++) {
+ isc_result_t result = add_qp(qp, n);
+ CHECK(n, result);
+ }
+ if (sqz) {
+ sqz_qp(qp);
+ }
+ dns_qpmulti_commit(arg->map, &qp);
+ if (brr) {
+ rcu_barrier();
+ }
+
+ isc_time_t t1 = isc_time_now_hires();
+
+ dns_qpread_t qpr;
+ dns_qpmulti_query(arg->map, &qpr);
+
+ for (size_t n = arg->start; n < arg->end; n++) {
+ void *pval = NULL;
+ isc_result_t result = get_qp(&qpr, n, &pval);
+ CHECK(n, result);
+ assert(pval == &item[n]);
+ }
+
+ dns_qpread_destroy(arg->map, &qpr);
+
+ isc_time_t t2 = isc_time_now_hires();
+
+ arg->d0 = isc_time_microdiff(&t1, &t0);
+ arg->d1 = isc_time_microdiff(&t2, &t1);
+
+ return (NULL);
+}
+
+static void *
+thread_qp(void *arg0) {
+ return (_thread_qp(arg0, true, false));
+}
+
+static void *
+thread_qp_nosqz(void *arg0) {
+ return (_thread_qp(arg0, false, false));
+}
+
+static void *
+thread_qp_brr(void *arg0) {
+ return (_thread_qp(arg0, true, true));
}
/*
* fun table
*/
-static struct fun {
- const char *name;
- void *(*new)(isc_mem_t *mem);
- isc_result_t (*add)(void *map, size_t count);
- void (*sqz)(void *map);
- isc_result_t (*get)(void *map, size_t count, void **pval);
-} fun_list[] = {
- { "ht", new_ht, add_ht, sqz_ht, get_ht },
- { "hashmap", new_hashmap, add_hashmap, sqz_hashmap, get_hashmap },
- { "rbt", new_rbt, add_rbt, sqz_rbt, get_rbt },
- { "qp", new_qp, add_qp, sqz_qp, get_qp },
- { NULL, NULL, NULL, NULL, NULL },
+static struct fun fun_list[] = {
+ { "lfht", new_lfht, thread_lfht },
+ { "ht", new_ht, thread_ht },
+ { "hashmap", new_hashmap, thread_hashmap },
+ { "rbt", new_rbt, thread_rbt },
+ { "qp", new_qp, thread_qp },
+ { "qp+nosqz", new_qp, thread_qp_nosqz },
+ { "qp+barrier", new_qp, thread_qp_brr },
+ { NULL, NULL, NULL },
};
-#define CHECK(result) \
- do { \
- if (result != ISC_R_SUCCESS) { \
- fprintf(stderr, "%s\n", isc_result_totext(result)); \
- exit(1); \
- } \
- } while (0)
-
#define FILE_CHECK(check, msg) \
do { \
if (!(check)) { \
size_t filesize, lines = 0, wirebytes = 0, labels = 0;
char *pos = NULL, *file_end = NULL;
+ isc_rwlock_init(&rwl);
+
isc_mem_create(&mctx);
if (argc != 2) {
- fprintf(stderr, "usage: load-names <filename.csv>\n");
+ fprintf(stderr,
+ "usage: load-names <filename.csv> <nthreads>\n");
exit(1);
}
lines++;
}
- printf("names %g MB labels %g MB\n", (double)wirebytes / 1048576.0,
+ printf("names %g MB labels %g MB\n\n", (double)wirebytes / 1048576.0,
(double)labels / 1048576.0);
- for (struct fun *fun = fun_list; fun->name != NULL; fun++) {
- isc_mem_t *mem = NULL;
- void *map = NULL;
+ printf("%10s | %10s | %10s | %10s | %10s | %10s | %10s |\n",
+ "algorithm", "threads", "load", "query", "dirty MB", "total",
+ "final MB");
- isc_mem_create(&mem);
- map = fun->new (mem);
+ for (size_t nthreads = 128; nthreads > 0; nthreads /= 2) {
+ printf("---------- | ---------- | ---------- | ---------- | "
+ "---------- | ---------- | ---------- |\n");
- isc_time_t t0 = isc_time_now_hires();
- for (size_t n = 0; n < lines; n++) {
- result = fun->add(map, n);
- CHECK(result);
- }
- fun->sqz(map);
-
- isc_time_t t1 = isc_time_now_hires();
- for (size_t n = 0; n < lines; n++) {
- void *pval = NULL;
- result = fun->get(map, n, &pval);
- CHECK(result);
- assert(pval == &item[n]);
- }
+ for (struct fun *fun = fun_list; fun->name != NULL; fun++) {
+ isc_mem_t *mem = NULL;
+ void *map = NULL;
+
+ isc_mem_create(&mem);
+ map = fun->new (mem);
+
+ size_t nitems = ARRAY_SIZE(item) / (nthreads + 1);
+
+ isc_barrier_init(&barrier, nthreads);
+
+ isc_time_t t0 = isc_time_now_hires();
+ size_t m0 = isc_mem_inuse(mem);
+
+ for (size_t i = 0; i < nthreads; i++) {
+ threads[i] = (struct thread_s){
+ .fun = fun,
+ .map = map,
+ .start = nitems * i,
+ .end = nitems * i + nitems,
+ };
+ isc_thread_create(fun->thread, &threads[i],
+ &threads[i].thread);
+ }
- isc_time_t t2 = isc_time_now_hires();
- printf("%f sec to load %s\n",
- (double)isc_time_microdiff(&t1, &t0) / (1000.0 * 1000.0),
- fun->name);
- printf("%f sec to query %s\n",
- (double)isc_time_microdiff(&t2, &t1) / (1000.0 * 1000.0),
- fun->name);
- printf("%g MB used by %s\n",
- (double)isc_mem_inuse(mem) / (1024.0 * 1024.0),
- fun->name);
+ uint64_t d0 = 0;
+ uint64_t d1 = 0;
+
+ for (size_t i = 0; i < nthreads; i++) {
+ isc_thread_join(threads[i].thread, NULL);
+ d0 += threads[i].d0;
+ d1 += threads[i].d1;
+ }
+
+ size_t m1 = isc_mem_inuse(mem);
+
+ rcu_barrier();
+
+ isc_time_t t1 = isc_time_now_hires();
+ uint64_t d3 = isc_time_microdiff(&t1, &t0);
+ size_t m2 = isc_mem_inuse(mem);
+
+ printf("%10s | %10zu | %10.4f | %10.4f | %10.4f | "
+ "%10.4f | %10.4f |\n",
+ fun->name, nthreads,
+ (double)(d0 / nthreads) / (1000.0 * 1000.0),
+ (double)(d1 / nthreads) / (1000.0 * 1000.0),
+ (double)(m1 - m0) / (1024.0 * 1024.0),
+ (double)d3 / (1000.0 * 1000.0),
+ (double)(m2 - m0) / (1024.0 * 1024.0)
+
+ );
+ }
}
+
+ printf("---------- | ---------- | ---------- | ---------- | "
+ "---------- | ---------- | ---------- |\n");
}