/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qp_p.h" #include #include struct { const char *text; dns_fixedname_t fixed; } item[1024 * 1024]; static void item_check(void *ctx, void *pval, uint32_t ival) { UNUSED(ctx); assert(pval == &item[ival]); } static size_t item_makekey(dns_qpkey_t key, void *ctx, void *pval, uint32_t ival) { UNUSED(ctx); assert(pval == &item[ival]); return (dns_qpkey_fromname(key, &item[ival].fixed.name)); } static void testname(void *ctx, char *buf, size_t size) { REQUIRE(ctx == NULL); strlcpy(buf, "test", size); } const dns_qpmethods_t qpmethods = { item_check, item_check, item_makekey, testname, }; /* * hashmap */ static void * new_hashmap(isc_mem_t *mem) { isc_hashmap_t *hashmap = NULL; isc_hashmap_create(mem, 16, 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); } 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)); } /* * ht */ static void * new_ht(isc_mem_t *mem) { isc_ht_t *ht = NULL; isc_ht_init(&ht, mem, 16, 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); } 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)); } /* * rbt */ static void * new_rbt(isc_mem_t *mem) { dns_rbt_t *rbt = NULL; (void)dns_rbt_create(mem, NULL, NULL, &rbt); return (rbt); } 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); } 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)); } /* * qp */ static void * new_qp(isc_mem_t *mem) { dns_qp_t *qp = NULL; dns_qp_create(mem, &qpmethods, NULL, &qp); return (qp); } static isc_result_t add_qp(void *qp, size_t count) { return (dns_qp_insert(qp, &item[count], count)); } static void sqz_qp(void *qp) { dns_qp_compact(qp, DNS_QPGC_ALL); } 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)); } /* * 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 }, }; #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)) { \ fprintf(stderr, "%s:%zu: %s\n", filename, lines, msg); \ exit(1); \ } \ } while (0) int main(int argc, char *argv[]) { isc_result_t result; const char *filename = NULL; char *filetext = NULL; off_t fileoff; FILE *fp = NULL; size_t filesize, lines = 0, wirebytes = 0, labels = 0; char *pos = NULL, *file_end = NULL; isc_mem_create(&mctx); if (argc != 2) { fprintf(stderr, "usage: load-names \n"); exit(1); } filename = argv[1]; result = isc_file_getsize(filename, &fileoff); if (result != ISC_R_SUCCESS) { fprintf(stderr, "stat(%s): %s\n", filename, isc_result_totext(result)); exit(1); } filesize = (size_t)fileoff; filetext = isc_mem_get(mctx, filesize + 1); fp = fopen(filename, "r"); if (fp == NULL || fread(filetext, 1, filesize, fp) < filesize) { fprintf(stderr, "read(%s): %s\n", filename, strerror(errno)); exit(1); } fclose(fp); filetext[filesize] = '\0'; pos = filetext; file_end = pos + filesize; while (pos < file_end) { char *domain = NULL, *newline = NULL; size_t len; FILE_CHECK(lines < ARRAY_SIZE(item), "too many lines"); pos += strspn(pos, "0123456789"); FILE_CHECK(*pos++ == ',', "missing comma"); domain = pos; pos += strcspn(pos, "\r\n"); FILE_CHECK(*pos != '\0', "missing newline"); newline = pos; pos += strspn(pos, "\r\n"); len = newline - domain; item[lines].text = domain; domain[len] = '\0'; dns_name_t *name = dns_fixedname_initname(&item[lines].fixed); isc_buffer_t buffer; isc_buffer_init(&buffer, domain, len); isc_buffer_add(&buffer, len); result = dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL); FILE_CHECK(result == ISC_R_SUCCESS, isc_result_totext(result)); wirebytes += name->length; labels += name->labels; lines++; } printf("names %g MB labels %g MB\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; isc_mem_create(&mem); map = fun->new (mem); 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]); } 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); } }