From: Vladimír Čunát Date: Wed, 31 Aug 2016 15:18:45 +0000 (+0200) Subject: add simple LRU benchmarks X-Git-Tag: v1.2.0-rc1~88^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2bdb7afd12a38efb6d732b775987d6d20d20dd7;p=thirdparty%2Fknot-resolver.git add simple LRU benchmarks The Makefile isn't perfect. I noted it doesn't clean the bench, but we have the same problem for some other parts, e.g. in deckard. --- diff --git a/Makefile b/Makefile index 1763d5aec..3b1896e2a 100644 --- a/Makefile +++ b/Makefile @@ -148,3 +148,4 @@ include modules/modules.mk include tests/tests.mk include doc/doc.mk include etc/etc.mk +include bench/bench.mk diff --git a/bench/bench.mk b/bench/bench.mk new file mode 100644 index 000000000..e06f5798a --- /dev/null +++ b/bench/bench.mk @@ -0,0 +1,27 @@ + +bench_BIN := \ + bench_lru + +# Dependencies +bench_DEPEND := $(libkres) +bench_LIBS := $(libkres_TARGET) $(libkres_LIBS) + +# Platform-specific library injection +ifeq ($(PLATFORM),Darwin) + preload_syms := DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_LIBRARY_PATH="$(DYLD_LIBRARY_PATH):$(abspath lib)" +else + preload_syms := LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):$(abspath lib)" +endif + +# Make bench binaries +define make_bench +$(1)_CFLAGS := -fPIC +$(1)_SOURCES := bench/$(1).c +$(1)_LIBS := $(bench_LIBS) +$(1)_DEPEND := $(bench_DEPEND) +$(call make_bin,$(1),bench) +.PHONY: $(1) +endef + +$(foreach bench,$(bench_BIN),$(eval $(call make_bench,$(bench)))) + diff --git a/bench/bench_lru.c b/bench/bench_lru.c new file mode 100644 index 000000000..cb2f75cd8 --- /dev/null +++ b/bench/bench_lru.c @@ -0,0 +1,206 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "contrib/ucw/lib.h" +#include "daemon/engine.h" +#include "lib/nsrep.h" + +typedef kr_nsrep_lru_t lru_bench_t; + + +static int die(const char *cause) { + fprintf(stderr, "%s: %s\n", cause, strerror(errno)); + exit(1); +} + +static void time_get(struct timeval *tv) { + if (gettimeofday(tv, NULL)) + die("gettimeofday"); +} +static void time_print_diff(struct timeval *tv, size_t op_count) { + struct timeval now; + time_get(&now); + now.tv_sec -= tv->tv_sec; + now.tv_usec -= tv->tv_usec; + if (now.tv_usec < 0) { + now.tv_sec -= 1; + now.tv_usec += 1000000; + } + + size_t speed = round((double)(op_count) / 1000 + / (now.tv_sec + (double)(now.tv_usec)/1000000)); + printf("\t%ld.%06d s, \t %zd kop/s\n", now.tv_sec, (int)now.tv_usec, speed); +} + +/// initialize seed for random() +static int ssrandom(char *s) { + if (*s == '-') { // initialize from time + struct timeval now; + time_get(&now); + srandom(now.tv_sec * 1000000 + now.tv_usec); + return 0; + } + + // initialize from a string + size_t len = strlen(s); + if (len < 12) + return(-1); + unsigned seed = s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + initstate(seed, s+4, len-4); + return 0; +} + +struct key { + size_t len; + char *chars; +}; + +/// read lines from a file and reorder them randomly +static struct key * read_lines(const char *fname, size_t *count) { + // read the file at once + int fd = open(fname, O_RDONLY); + if (fd < 0) + die("open"); + struct stat st; + if (fstat(fd, &st) < 0) + die("stat"); + size_t flen = (size_t)st.st_size; + char *fbuf = malloc(flen + 1); + if (fbuf == NULL) + die("malloc"); + if (read(fd, fbuf, flen) < 0) + die("read"); + close(fd); + fbuf[flen] = '\0'; + + // get pointers to individual lines + size_t lines = 0; + for (size_t i = 0; i < flen; ++i) + if (fbuf[i] == '\n') { + fbuf[i] = 0; + ++lines; + } + *count = lines; + size_t avg_len = (flen + 1) / lines - 1; + printf("%zu lines read, average length %zu\n", lines, avg_len); + + struct key *result = calloc(lines, sizeof(struct key)); + result[0].chars = fbuf; + for (size_t l = 0; l < lines; ++l) { + size_t i = 0; + while (result[l].chars[i]) + ++i; + result[l].len = i; + if (l + 1 < lines) + result[l + 1].chars = result[l].chars + i + 1; + } + + //return result; + // reorder the lines randomly (via "random select-sort") + // note: this makes their order non-sequential *in memory* + if (RAND_MAX < lines) + die("RAND_MAX is too small"); + for (size_t i = 0; i < lines - 1; ++i) { // swap i with random j >= i + size_t j = i + random() % (lines - i); + if (j != i) { + struct key tmp = result[i]; + result[i] = result[j]; + result[j] = tmp; + } + } + + return result; +} + +// compatibility layer for the oler lru_* names; it's more compler with lru_create +#ifndef lru_create + #define lru_get_new lru_set + #define lru_get_try lru_get +#endif + +static void usage(const char *progname) { + fprintf(stderr, "usage: %s \n" + "The seed must be at least 12 characters or \"-\".\n" , progname); + exit(1); +} + +int main(int argc, char ** argv) { + if (argc != 4 && argc != 5) + usage(argv[0]); + if (ssrandom(argv[3]) < 0) + usage(argv[0]); + + size_t key_count; + struct key *keys = read_lines(argv[2], &key_count); + size_t run_count; + { + size_t run_log = atoi(argv[1]); + run_count = 1 << run_log; + printf("test run length: 2^%zd\n", run_log); + } + + struct timeval time; + const int lru_size = argc > 4 ? atoi(argv[4]) : LRU_RTT_SIZE; + + lru_bench_t *lru; + #ifdef lru_create + lru_create(&lru, lru_size, NULL); + #else + lru = malloc(lru_size(lru_bench_t, lru_size)); + if (lru) + lru_init(lru, lru_size); + #endif + if (!lru) + die("malloc"); + printf("LRU size:\t%d\n", lru_size); + + size_t miss = 0; + time_get(&time); + printf("load everything:"); + for (size_t i = 0, ki = key_count; i < run_count; ++i, --ki) { + unsigned *r = lru_get_new(lru, keys[ki].chars, keys[ki].len); + if (!r || *r == 0) + ++miss; + if (r) + *r = 1; + if (unlikely(ki == 0)) + ki = key_count; + } + time_print_diff(&time, run_count); + printf("LRU misses:\t%zd%%\n", (miss * 100 + 50) / run_count); + + unsigned accum = 0; // compute something to make sure compiler can't remove code + time_get(&time); + printf("search everything:"); + for (size_t i = 0, ki = key_count; i < run_count; ++i, --ki) { + unsigned *r = lru_get_try(lru, keys[ki].chars, keys[ki].len); + if (r) + accum += *r; + if (unlikely(ki == 0)) + ki = key_count; + } + time_print_diff(&time, run_count); + printf("ignore: %u\n", accum); + + return 0; +} +