Commit f2bdb7af authored by Vladimír Čunát's avatar Vladimír Čunát

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.
parent 30eb8bd5
......@@ -148,3 +148,4 @@ include modules/modules.mk
include tests/tests.mk
include doc/doc.mk
include etc/etc.mk
include bench/bench.mk
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))))
/* 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment