]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
add simple LRU benchmarks
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 31 Aug 2016 15:18:45 +0000 (17:18 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 2 Nov 2016 10:14:05 +0000 (11:14 +0100)
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.

Makefile
bench/bench.mk [new file with mode: 0644]
bench/bench_lru.c [new file with mode: 0644]

index 1763d5aec4c992cb6f939a3a247da0bc231ce0c0..3b1896e2ae38267b61e74a13d03988d94bd02bd6 100644 (file)
--- 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 (file)
index 0000000..e06f579
--- /dev/null
@@ -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 (file)
index 0000000..cb2f75c
--- /dev/null
@@ -0,0 +1,206 @@
+/*  Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#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 <seed> <input>\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;
+}
+