1
0
mirror of https://github.com/rtbrick/bngblaster.git synced 2024-05-06 15:54:57 +00:00
Files
2023-12-21 11:03:22 +00:00

457 lines
12 KiB
C

/* benchmark.c
* Some timing tests for libdict
* Copyright (C) 2001-2011 Farooq Mela */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "dict.h"
#include "dict_private.h"
#include "tree_common.h"
#include "util.h"
static const char appname[] = "benchmark";
char *xstrdup(const char *str);
#ifdef __GNUC__
# define NORETURN __attribute__((__noreturn__))
#else
# define NORETURN
#endif
void quit(const char *, ...) NORETURN;
void warn(const char *fmt, ...);
void *xmalloc(size_t size);
static size_t hash_count = 0, comp_count = 0;
unsigned str_hash(const void *p);
int my_strcmp(const void *k1, const void *k2);
unsigned ptr_hash(const void *p);
int my_ptrcmp(const void *k1, const void *k2);
void key_str_free(void *key, void *datum);
void timer_start(struct rusage* start);
void timer_end(const struct rusage* start, struct rusage *end,
struct timeval *total);
dict *create_dictionary(char type, const char **container_name);
#define HASHTABLE_SIZE 97
/* #define HASHTABLE_SIZE 997 */
/* #define HASHTABLE_SIZE 9973 */
static size_t malloced = 0;
int
main(int argc, char **argv)
{
bool shuffle_keys = true;
if (argc != 3) {
fprintf(stderr, "usage: %s [type] [input]\n", appname);
fprintf(stderr, "type: specifies the dictionary type:\n");
fprintf(stderr, " h: height-balanced tree\n");
fprintf(stderr, " p: path-reduction tree\n");
fprintf(stderr, " r: red-black tree\n");
fprintf(stderr, " t: treap\n");
fprintf(stderr, " s: splay tree\n");
fprintf(stderr, " w: weight-balanced tree\n");
fprintf(stderr, " S: skiplist\n");
fprintf(stderr, " H: hashtable\n");
fprintf(stderr, " 2: hashtable 2\n");
fprintf(stderr, "input: text file consisting of newline-separated keys\n");
exit(EXIT_FAILURE);
}
srand(0xdeadbeef);
dict_malloc_func = xmalloc;
const char type = argv[1][0];
const char *container_name = NULL;
dict *dct = create_dictionary(type, &container_name);
if (!dct)
quit("can't create container");
ASSERT(dict_verify(dct));
ASSERT(comp_count == 0);
ASSERT(hash_count == 0);
const size_t malloced_save = malloced;
FILE *fp = fopen(argv[2], "r");
if (fp == NULL)
quit("cant open file '%s': %s", argv[2], strerror(errno));
size_t nwords = 0;
char buf[512];
while (fgets(buf, sizeof(buf), fp))
++nwords;
if (!nwords)
quit("nothing read from file");
char **words = xmalloc(sizeof(*words) * nwords);
rewind(fp);
size_t words_read = 0;
while (words_read < nwords && fgets(buf, sizeof(buf), fp)) {
strtok(buf, "\n");
words[words_read++] = xstrdup(buf);
}
fclose(fp);
if (words_read < nwords)
quit("Only read %zu/%zu words!", words_read, nwords);
printf("Loaded %zu keys from %s.\n", nwords, argv[2]);
malloced = malloced_save;
size_t total_comp = 0, total_hash = 0, total_rotations = 0;
struct rusage start, end;
struct timeval total = { 0, 0 };
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
dict_insert_result result = dict_insert(dct, words[i]);
if (!result.inserted)
quit("insert #%d failed for '%s'", i, words[i]);
ASSERT(result.datum_ptr != NULL);
ASSERT(*result.datum_ptr == NULL);
*result.datum_ptr = words[i];
}
timer_end(&start, &end, &total);
printf(" %s container: %.02fkB\n", container_name, malloced_save * 1e-3);
printf(" %s memory: %.02fkB\n", container_name, malloced * 1e-3);
printf(" %s insert: %6.03fs %9zu cmp (%.02f/insert)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (dict_is_sorted(dct) && type != 'S') {
tree_base *tree = dict_private(dct);
printf(" min path length: %zu\n", tree_min_path_length(tree));
printf(" max path length: %zu\n", tree_max_path_length(tree));
printf(" tot path length: %zu\n", tree_total_path_length(tree));
printf("insert rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
} else if (type == 'S') {
size_t counts[16] = { 0 };
size_t num_counts = skiplist_link_count_histogram(dict_private(dct), counts, sizeof(counts) / sizeof(counts[0]));
size_t count_sum = 0;
for (size_t i = 0; i <= num_counts; ++i) {
printf("skiplist %zu-node(s): %zu\n", i, counts[i]);
count_sum += counts[i];
}
ASSERT(count_sum == nwords);
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
size_t n = dict_count(dct);
if (n != nwords)
quit("bad count (%u - should be %u)!", n, nwords);
dict_itor *itor = dict_itor_new(dct);
timer_start(&start);
n = 0;
ASSERT(dict_itor_first(itor));
do {
ASSERT(dict_itor_valid(itor));
ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor));
++n;
} while (dict_itor_next(itor));
timer_end(&start, &end, &total);
printf(" %s fwd iterate: %6.03fs\n",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6);
if (n != nwords)
warn("Fwd iteration returned %u items - should be %u", n, nwords);
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
n = 0;
ASSERT(dict_itor_last(itor));
do {
ASSERT(dict_itor_valid(itor));
ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor));
++n;
} while (dict_itor_prev(itor));
timer_end(&start, &end, &total);
printf(" %s rev iterate: %6.03fs\n",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6);
if (n != nwords)
warn("Rev iteration returned %u items - should be %u", n, nwords);
dict_itor_free(itor);
if (shuffle_keys) shuffle(words, nwords);
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
void **p = dict_search(dct, words[i]);
if (!p)
quit("lookup failed for '%s'", buf);
if (*p != words[i])
quit("bad data for '%s', got '%s' instead", words[i], *(char **)p);
}
timer_end(&start, &end, &total);
printf(" %s good search: %6.03fs %9zu cmp (%.02f/search)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (type != 'H' && type != '2' && type != 'S') {
tree_base *tree = dict_private(dct);
printf("search rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
unsigned rv = dict_rand() % strlen(words[i]);
words[i][rv]++;
dict_search(dct, words[i]);
words[i][rv]--;
}
timer_end(&start, &end, &total);
printf(" %s bad search: %6.03fs %9zu cmp (%.02f/search)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
if (shuffle_keys) shuffle(words, nwords);
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
dict_remove_result result = dict_remove(dct, words[i]);
if (!result.removed)
quit("removing #%d '%s' failed!\n", i, words[i]);
ASSERT(result.key == words[i]);
ASSERT(result.datum == words[i]);
}
timer_end(&start, &end, &total);
printf(" %s remove: %6.03fs %9zu cmp (%.2f/remove)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double)nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (type != 'H' && type != '2' && type != 'S') {
tree_base *tree = dict_private(dct);
printf("remove rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
if ((n = dict_count(dct)) != 0)
quit("error - count not zero (%u)!", n);
dict_free(dct, key_str_free);
printf(" %s total: %6.03fs %9zu cmp",
container_name,
(total.tv_sec * 1000000 + total.tv_usec) * 1e-6,
total_comp);
if (total_hash)
printf(" %9zu hash", total_hash);
printf("\n");
if (type != 'H' && type != '2' && type != 'S') {
printf(" total rotations: %zu\n", total_rotations);
}
FREE(words);
exit(EXIT_SUCCESS);
}
dict *
create_dictionary(char type, const char **container_name)
{
dict_compare_func cmp_func = my_strcmp;
dict_hash_func hash_func = str_hash;
switch (type) {
case 'h':
*container_name = "hb";
return hb_dict_new(cmp_func);
case 'p':
*container_name = "pr";
return pr_dict_new(cmp_func);
case 'r':
*container_name = "rb";
return rb_dict_new(cmp_func);
case 't':
*container_name = "tr";
return tr_dict_new(cmp_func, NULL);
case 's':
*container_name = "sp";
return sp_dict_new(cmp_func);
case 'S':
*container_name = "sk";
return skiplist_dict_new(cmp_func, 12);
case 'w':
*container_name = "wb";
return wb_dict_new(cmp_func);
case 'H':
*container_name = "ht";
return hashtable_dict_new(cmp_func, hash_func, HASHTABLE_SIZE);
case '2':
*container_name = "h2";
return hashtable2_dict_new(cmp_func, hash_func, HASHTABLE_SIZE);
default:
quit("type must be one of h, p, r, t, s, w or H");
}
}
char *
xstrdup(const char *str)
{
size_t len = strlen(str) + 1;
return memcpy(xmalloc(len), str, len);
}
void
quit(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s: ", appname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(EXIT_FAILURE);
}
void
warn(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "warning: %s: ", appname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
void *
xmalloc(size_t size)
{
void *p = malloc(size);
if (!p)
quit("out of memory");
malloced += size;
return p;
}
unsigned
str_hash(const void *p)
{
++hash_count;
return dict_str_hash(p);
}
int
my_strcmp(const void *k1, const void *k2)
{
++comp_count;
return strcmp(k1, k2);
}
unsigned
ptr_hash(const void *p)
{
++hash_count;
return (unsigned) ((2166136261U ^ (uintptr_t)p) * 16777619U);
}
int
my_ptrcmp(const void *k1, const void *k2)
{
++comp_count;
return (k1 < k2) ? -1 : (k1 > k2);
}
void
key_str_free(void *key, void *datum)
{
ASSERT(key == datum);
free(key);
}
void
timer_start(struct rusage *start)
{
getrusage(RUSAGE_SELF, start);
}
void
timer_end(const struct rusage *start, struct rusage *end,
struct timeval *total) {
getrusage(RUSAGE_SELF, end);
if (end->ru_utime.tv_usec < start->ru_utime.tv_usec) {
end->ru_utime.tv_usec += 1000000; end->ru_utime.tv_sec--;
}
end->ru_utime.tv_usec -= start->ru_utime.tv_usec;
end->ru_utime.tv_sec -= start->ru_utime.tv_sec;
total->tv_sec += end->ru_utime.tv_sec;
if ((total->tv_usec += end->ru_utime.tv_usec) > 1000000) {
total->tv_usec -= 1000000; total->tv_sec++;
}
}