230 lines
5.1 KiB
C
230 lines
5.1 KiB
C
#include "../ufbx.c"
|
|
#include <inttypes.h>
|
|
|
|
typedef struct {
|
|
uint32_t begin;
|
|
uint32_t increment;
|
|
uint64_t attempts;
|
|
uint32_t *slots;
|
|
size_t num_slots;
|
|
size_t target_slot;
|
|
} hash_info;
|
|
|
|
typedef void hash_fn(hash_info info);
|
|
|
|
typedef struct {
|
|
char str[256];
|
|
uint32_t *slots;
|
|
size_t mask;
|
|
size_t max_length;
|
|
int64_t attempts_left;
|
|
size_t target_slot;
|
|
} str_state;
|
|
|
|
ufbxi_noinline void print_string(str_state *state, size_t length)
|
|
{
|
|
#if _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
{
|
|
state->str[length] = '\0';
|
|
puts(state->str);
|
|
}
|
|
}
|
|
|
|
ufbxi_noinline void hash_string_imp(str_state *state, size_t length)
|
|
{
|
|
if (state->attempts_left < 0) return;
|
|
state->attempts_left -= ('Z' - 'A' + 1) * 2;
|
|
|
|
uint32_t *slots = state->slots;
|
|
size_t mask = state->mask;
|
|
size_t target_slot = state->target_slot;
|
|
|
|
char *p = state->str + length - 1;
|
|
for (uint32_t c = 'A'; c <= 'Z'; c++) {
|
|
*p = c;
|
|
uint32_t hash = ufbxi_hash_string(state->str, length);
|
|
if ((hash & mask) == target_slot) print_string(state, length);
|
|
slots[hash & mask]++;
|
|
*p = c | 0x20;
|
|
hash = ufbxi_hash_string(state->str, length);
|
|
if ((hash & mask) == target_slot) print_string(state, length);
|
|
slots[hash & mask]++;
|
|
}
|
|
|
|
if (length < state->max_length) {
|
|
for (uint32_t c = 'A'; c <= 'Z'; c++) {
|
|
*p = c;
|
|
hash_string_imp(state, length + 1);
|
|
*p = c | 0x20;
|
|
hash_string_imp(state, length + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
ufbxi_noinline void hash_string(hash_info info)
|
|
{
|
|
size_t mask = info.num_slots - 1;
|
|
str_state state;
|
|
state.attempts_left = (int64_t)info.attempts;
|
|
state.mask = mask;
|
|
state.slots = info.slots;
|
|
state.target_slot = info.target_slot;
|
|
|
|
size_t max_len = 0;
|
|
uint64_t len_attempts = 1;
|
|
while (len_attempts < info.attempts) {
|
|
len_attempts *= ('Z' - 'A' + 1) * 2;
|
|
max_len += 1;
|
|
}
|
|
state.max_length = max_len;
|
|
|
|
for (uint32_t c = 'A' + info.begin; c <= 'Z'; c += info.increment) {
|
|
state.str[0] = c;
|
|
uint32_t hash = ufbxi_hash_string(state.str, 1);
|
|
if ((hash & mask) == info.target_slot) print_string(&state, 1);
|
|
info.slots[hash & mask]++;
|
|
if (max_len > 1) {
|
|
hash_string_imp(&state, 2);
|
|
}
|
|
|
|
state.str[0] = c | 0x20;
|
|
hash = ufbxi_hash_string(state.str, 1);
|
|
if ((hash & mask) == info.target_slot) print_string(&state, 1);
|
|
info.slots[hash & mask]++;
|
|
if (max_len > 1) {
|
|
hash_string_imp(&state, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
ufbxi_noinline void print_uint64(uint64_t v)
|
|
{
|
|
#if _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
{
|
|
printf("%" PRIu64 "\n", v);
|
|
}
|
|
}
|
|
|
|
|
|
ufbxi_noinline void hash_uint64(hash_info info)
|
|
{
|
|
size_t mask = info.num_slots - 1;
|
|
uint64_t increment = info.increment;
|
|
uint64_t end = info.attempts;
|
|
for (uint64_t i = info.begin; i < end; i += increment) {
|
|
uint32_t hash = ufbxi_hash64(i);
|
|
if ((hash & mask) == info.target_slot) print_uint64(i);
|
|
info.slots[hash & mask]++;
|
|
}
|
|
}
|
|
|
|
ufbxi_noinline void run_test(hash_info info, hash_fn *fn, uint32_t *accumulator)
|
|
{
|
|
info.slots = malloc(sizeof(uint32_t) * info.num_slots);
|
|
memset(info.slots, 0, sizeof(uint32_t) * info.num_slots);
|
|
fn(info);
|
|
|
|
#if _OPENMP
|
|
#pragma omp critical
|
|
#endif
|
|
{
|
|
for (size_t i = 0; i < info.num_slots; i++) {
|
|
accumulator[i] += info.slots[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
const char *name;
|
|
hash_fn *fn;
|
|
} hash_test;
|
|
|
|
hash_test tests[] = {
|
|
"string", &hash_string,
|
|
"uint64", &hash_uint64,
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
const char *func = "";
|
|
size_t num_slots = 0;
|
|
size_t target_slot = SIZE_MAX;
|
|
uint64_t attempts = 0;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (!strcmp(argv[i], "-f")) {
|
|
if (++i < argc) func = argv[i];
|
|
} else if (!strcmp(argv[i], "--slots")) {
|
|
if (++i < argc) num_slots = (size_t)strtoull(argv[i], NULL, 10);
|
|
} else if (!strcmp(argv[i], "--attempts")) {
|
|
if (++i < argc) attempts = (uint64_t)strtoull(argv[i], NULL, 10);
|
|
} else if (!strcmp(argv[i], "--target")) {
|
|
if (++i < argc) target_slot = (size_t)strtoull(argv[i], NULL, 10);
|
|
} else if (!strcmp(argv[i], "--threads")) {
|
|
#if _OPENMP
|
|
if (++i < argc) omp_set_num_threads(atoi(argv[i]));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
hash_fn *hash_fn = NULL;
|
|
for (size_t i = 0; i < ufbxi_arraycount(tests); i++) {
|
|
if (!strcmp(tests[i].name, func)) {
|
|
hash_fn = tests[i].fn;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hash_fn) {
|
|
fprintf(stderr, "Unkonwn hash function '%s'\n", func);
|
|
return 1;
|
|
}
|
|
|
|
if ((num_slots & (num_slots - 1)) != 0) {
|
|
fprintf(stderr, "Slot amount must be a power of two, got %zu\n", num_slots);
|
|
return 1;
|
|
}
|
|
|
|
uint32_t *slots = malloc(num_slots * sizeof(uint32_t));
|
|
memset(slots, 0, sizeof(uint32_t) * num_slots);
|
|
|
|
hash_info info;
|
|
info.attempts = (int64_t)attempts;
|
|
info.increment = 1;
|
|
info.num_slots = num_slots;
|
|
info.target_slot = target_slot;
|
|
|
|
#if _OPENMP
|
|
#pragma omp parallel
|
|
{
|
|
info.begin = (uint64_t)omp_get_thread_num();
|
|
info.increment = (uint64_t)omp_get_num_threads();
|
|
run_test(info, hash_fn, slots);
|
|
}
|
|
#else
|
|
{
|
|
info.begin = 0;
|
|
info.increment = 1;
|
|
run_test(info, hash_fn, slots);
|
|
}
|
|
#endif
|
|
|
|
uint32_t max_collisions = 0;
|
|
size_t worst_slot = 0;
|
|
for (size_t i = 0; i < num_slots; i++) {
|
|
if (slots[i] > max_collisions) {
|
|
max_collisions = slots[i];
|
|
worst_slot = i;
|
|
}
|
|
}
|
|
|
|
if (target_slot == SIZE_MAX) {
|
|
printf("Worst slot: %zu (%u collisions)\n", worst_slot, max_collisions);
|
|
}
|
|
return 0;
|
|
}
|