Last active
March 2, 2022 14:50
-
-
Save jrepp/2981eec90bba881696256605722e94d7 to your computer and use it in GitHub Desktop.
Multiplicative hash test program that stacks a few algorithms against each other and shows their relative collisions and table distributions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a file that produces statistics for various integer multiplicative and mix functions to profile their performance | |
// for a representative problem (apriltag cluster hashing) | |
// | |
// the output of this program is a CSV of hash collisions and depth (including p90 depth) | |
// additional the full hash table distributions for each sample set is printed | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <assert.h> | |
#include <math.h> | |
typedef uint32_t (*cluster_hash_func)(uint32_t x, uint32_t y); | |
struct cluster_hash_func_desc { | |
const char *name; | |
cluster_hash_func h; | |
}; | |
struct cluster_hash { | |
cluster_hash_func h; | |
uint32_t *bins; | |
uint32_t nbins; | |
uint32_t items; | |
uint32_t collisions; | |
uint32_t maxbin; | |
}; | |
struct distribution { | |
const char *name; | |
uint32_t size; | |
uint32_t clusters; | |
uint32_t nbins; | |
uint32_t values[0]; | |
}; | |
uint32_t u64hash_2(uint32_t x, uint32_t y) { | |
uint64_t v = ((uint64_t)x) << 32 | y; | |
return (2654435761 * v) >> 32; | |
} | |
uint32_t knuth_mul(uint32_t x, uint32_t y) { | |
uint64_t v = ((uint64_t)x) << 32 | y; | |
return (2654435769 * v) >> 32; | |
} | |
uint32_t mix(uint32_t x, uint32_t y) { | |
uint32_t v = (x * 4294967311) ^ (y * 4294967311); | |
return v; | |
} | |
uint32_t mix2(uint32_t x, uint32_t y) { | |
x = ((x >> 16) ^ x) * 0x45d9f3b; | |
x = ((x >> 16) ^ x) * 0x45d9f3b; | |
x = (x >> 16) ^ x; | |
y = ((y >> 16) ^ y) * 0x45d9f3b; | |
y = ((y >> 16) ^ y) * 0x45d9f3b; | |
y = (y >> 16) ^ y; | |
return x ^ y; | |
} | |
void cluster_hash_init(struct cluster_hash *ht, uint32_t nbins) { | |
memset(ht, 0, sizeof(struct cluster_hash)); | |
ht->bins = calloc(nbins, sizeof(ht->bins[0])); | |
ht->nbins = nbins; | |
} | |
void cluster_hash_deinit(struct cluster_hash *h) { | |
free(h->bins); | |
} | |
void hash_one_coord(struct cluster_hash *ht, uint32_t x, uint32_t y) { | |
uint32_t hvalue = ht->h(x, y); | |
uint32_t bin = hvalue % ht->nbins; | |
if (ht->bins[bin]) { | |
ht->collisions++; | |
} | |
ht->bins[bin]++; | |
ht->items++; | |
if (ht->maxbin < ht->bins[bin]) { | |
ht->maxbin = ht->bins[bin]; | |
} | |
} | |
void process_image(struct cluster_hash *ht, uint32_t w, uint32_t h, uint32_t clusters) { | |
srandom(612341); | |
for (uint32_t c = 0; c < clusters; ++c) { | |
uint32_t x = rand() % w; | |
uint32_t y = rand() % h; | |
hash_one_coord(ht, x, y); | |
} | |
} | |
struct distribution* create_distribution(struct cluster_hash *ht) { | |
struct distribution *d = (struct distribution *)calloc(1, | |
sizeof(struct distribution) + ((ht->maxbin + 1) * sizeof(uint32_t))); | |
d->size = ht->maxbin + 1; | |
for (uint32_t i = 0; i < ht->nbins; ++i) { | |
d->values[ht->bins[i]]++; | |
} | |
return d; | |
} | |
void print_distribution(struct distribution *d) { | |
for (uint32_t i = 0; i < d->size; ++i) { | |
printf("%5u, ", d->values[i]); | |
} | |
} | |
float calc_p(float p, struct distribution *d, uint32_t sample_size) { | |
float pop = 0; | |
float target_pop = (float)(sample_size) * (p/100.0); | |
float thresh = 0; | |
for (uint32_t i = 0; i < d->size; ++i) { | |
if (pop + (float)d->values[i] < target_pop) { | |
pop += (float)d->values[i]; | |
thresh = (float)i; | |
} else { | |
float left = target_pop - pop; | |
float delta = (pop + d->values[i]) - pop; | |
thresh += left / delta; | |
break; | |
} | |
} | |
return thresh; | |
} | |
int main() { | |
struct cluster_hash ht; | |
struct cluster_hash_func_desc funcs[] = { | |
{ "default", u64hash_2 }, | |
{ "knuth_mul", knuth_mul }, | |
{ "mix", mix }, | |
{ "mix2", mix2 }, | |
}; | |
// w, h, clusters, nclustermap | |
uint32_t image_sizes[][4] = { | |
// nori-5-640.jpg: for single tag in image with default settings | |
// 240x320 on threshim 118 clusters, nclustermap 15360, orig 480x640 | |
{ 640, 480, 118, 15360 }, // captured | |
{ 640, 480, 118 * 3, 15360 }, // 3x captured | |
{ 640, 480, 118, 15361 }, // prime nclustermap | |
{ 640, 480, 118, 256 }, // reduced nclustermap | |
{ 640, 480, 118, 257 }, // reduced prime nclustermap | |
{ 640, 480, 118 * 3, 256 }, // 3x clusters | |
{ 640, 480, 118 * 3, 257 }, // 3x clusters | |
// crash-april.pnm: multiple tags in noisy black image | |
// 1224x1024 on threshim 1435 clusters, nclustermap 250675 | |
{ 2448, 2048, 1435, 250675 }, // captured | |
{ 2448, 2048, 1435 * 3, 250675 }, // 3x captured | |
{ 2448, 2048, 1435, 250681 }, // prime nclustermap | |
{ 2448, 2048, 1435, 1024 }, // reduced nclustermap | |
{ 2448, 2048, 1435, 1031 }, // reduced prime nclustermap | |
{ 2448, 2048, 1435 * 3, 1024 }, // 3x clusters, reduced | |
{ 2448, 2048, 1435 * 3, 1031 }, // 3x clusters, reduced prime | |
// nori-5-orig.jpg: same image at higher resolution (single tag | |
// 2016x1512 threshim 5932 clusters, nclustermap 609638 | |
{ 4032, 3024, 5932, 609638 }, // captured | |
{ 4032, 3024, 5932 * 3, 609638 }, // 3x captured | |
{ 4032, 3024, 5932, 609641 }, // prime nclustermap | |
{ 4032, 3024, 5932, 2048 }, // reduced nclustermap | |
{ 4032, 3024, 5932, 2053 }, // reduced prime nclustermap | |
{ 4032, 3024, 5932 * 3, 2048 }, // 3x clusters, reduced nclustermap | |
{ 4032, 3024, 5932 * 3, 2053 }, // 3x clusters, reduced prime nclustermap | |
}; | |
uint32_t nfuncs = sizeof(funcs)/sizeof(funcs[0]); | |
uint32_t ntests = sizeof(image_sizes)/sizeof(image_sizes[0]); | |
printf("---------------- cluster hashing ------------------------\n"); | |
printf("w, h, clusters, nbins, hash, collisions, maxbin, p90\n"); | |
struct distribution **distributions = (struct distribution **)calloc( | |
nfuncs * ntests, sizeof(struct distribution*)); | |
uint32_t dist_i = 0; | |
for (uint32_t t = 0; t < ntests; ++t) { | |
uint32_t w = image_sizes[t][0], | |
h = image_sizes[t][1], | |
c = image_sizes[t][2], | |
n = image_sizes[t][3]; | |
printf("%5u, %5u, %5u, %8u, ", | |
w, | |
h, | |
c, | |
n); | |
for (uint32_t f = 0; f < nfuncs; ++f) { | |
cluster_hash_init(&ht, n); | |
ht.h = funcs[f].h; | |
// threshim coords are /2 | |
process_image(&ht, w / 2, h / 2, c); | |
struct distribution *d = create_distribution(&ht); | |
float p90 = calc_p(90.0, d, ht.nbins); | |
d->name = funcs[f].name; | |
d->clusters = c; | |
d->nbins = ht.nbins; | |
printf("%s, %5u, %3u, %3.3f, ", | |
funcs[f].name, | |
ht.collisions, | |
ht.maxbin, | |
p90); | |
distributions[dist_i++] = d; | |
cluster_hash_deinit(&ht); | |
} | |
printf("\n"); | |
} | |
for (uint32_t i = 0; i < dist_i; ++i) { | |
struct distribution *d = distributions[i]; | |
printf("%12s (c: %6u n: %6u): ", d->name, d->clusters, d->nbins); | |
print_distribution(d); | |
printf("\n"); | |
free(d); | |
} | |
free(distributions); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---------------- cluster hashing ------------------------ | |
w, h, clusters, nbins, hash, collisions, maxbin, p90 | |
640, 480, 118, 15360, default, 0, 1, 0.907, knuth_mul, 0, 1, 0.907, mix, 1, 2, 0.907, mix2, 1, 2, 0.907, | |
640, 480, 354, 15360, default, 8, 2, 0.921, knuth_mul, 4, 2, 0.921, mix, 18, 3, 0.920, mix2, 4, 3, 0.921, | |
640, 480, 118, 15361, default, 1, 2, 0.907, knuth_mul, 0, 1, 0.907, mix, 1, 2, 0.907, mix2, 0, 1, 0.907, | |
640, 480, 118, 256, default, 28, 3, 0.991, knuth_mul, 23, 4, 0.890, mix, 22, 3, 0.903, mix2, 20, 3, 0.905, | |
640, 480, 118, 257, default, 21, 3, 0.914, knuth_mul, 25, 5, 0.886, mix, 29, 4, 0.959, mix2, 20, 3, 0.904, | |
640, 480, 354, 256, default, 168, 6, 2.650, knuth_mul, 165, 6, 2.669, mix, 158, 5, 2.576, mix2, 159, 6, 2.497, | |
640, 480, 354, 257, default, 159, 7, 2.475, knuth_mul, 165, 6, 2.630, mix, 180, 10, 2.721, mix2, 159, 5, 2.412, | |
2448, 2048, 1435, 250675, default, 3, 2, 0.905, knuth_mul, 7, 2, 0.905, mix, 70, 4, 0.905, mix2, 9, 4, 0.905, | |
2448, 2048, 4305, 250675, default, 41, 2, 0.916, knuth_mul, 51, 3, 0.916, mix, 611, 6, 0.913, mix2, 52, 6, 0.916, | |
2448, 2048, 1435, 250681, default, 6, 2, 0.905, knuth_mul, 6, 2, 0.905, mix, 70, 4, 0.905, mix2, 11, 4, 0.905, | |
2448, 2048, 1435, 1024, default, 667, 7, 2.591, knuth_mul, 665, 5, 2.651, mix, 665, 8, 2.592, mix2, 664, 7, 2.573, | |
2448, 2048, 1435, 1031, default, 654, 7, 2.521, knuth_mul, 657, 8, 2.635, mix, 657, 8, 2.594, mix2, 658, 6, 2.560, | |
2448, 2048, 4305, 1024, default, 3300, 12, 6.450, knuth_mul, 3299, 12, 6.539, mix, 3292, 11, 6.478, mix2, 3306, 12, 6.637, | |
2448, 2048, 4305, 1031, default, 3291, 13, 6.271, knuth_mul, 3291, 13, 6.460, mix, 3289, 13, 6.427, mix2, 3294, 12, 6.592, | |
4032, 3024, 5932, 609638, default, 38, 2, 0.909, knuth_mul, 51, 2, 0.909, mix, 672, 4, 0.908, mix2, 37, 3, 0.909, | |
4032, 3024, 17796, 609638, default, 345, 3, 0.927, knuth_mul, 371, 3, 0.926, mix, 5142, 10, 0.919, mix2, 338, 10, 0.927, | |
4032, 3024, 5932, 609641, default, 29, 2, 0.909, knuth_mul, 38, 2, 0.909, mix, 672, 4, 0.908, mix2, 37, 3, 0.909, | |
4032, 3024, 5932, 2048, default, 3992, 9, 4.698, knuth_mul, 3995, 10, 4.690, mix, 3998, 10, 4.738, mix2, 3994, 10, 4.682, | |
4032, 3024, 5932, 2053, default, 3997, 13, 4.721, knuth_mul, 3992, 9, 4.783, mix, 3991, 12, 4.757, mix2, 3998, 11, 4.712, | |
4032, 3024, 17796, 2048, default, 15748, 24, 12.058, knuth_mul, 15749, 23, 12.165, mix, 15749, 20, 12.040, mix2, 15748, 23, 12.047, | |
4032, 3024, 17796, 2053, default, 15743, 20, 12.224, knuth_mul, 15744, 20, 12.211, mix, 15743, 23, 12.297, mix2, 15743, 23, 11.938, | |
default (c: 118 n: 15360): 15242, 118, | |
knuth_mul (c: 118 n: 15360): 15242, 118, | |
mix (c: 118 n: 15360): 15243, 116, 1, | |
mix2 (c: 118 n: 15360): 15243, 116, 1, | |
default (c: 354 n: 15360): 15014, 338, 8, | |
knuth_mul (c: 354 n: 15360): 15010, 346, 4, | |
mix (c: 354 n: 15360): 15024, 319, 16, 1, | |
mix2 (c: 354 n: 15360): 15010, 347, 2, 1, | |
default (c: 118 n: 15361): 15244, 116, 1, | |
knuth_mul (c: 118 n: 15361): 15243, 118, | |
mix (c: 118 n: 15361): 15244, 116, 1, | |
mix2 (c: 118 n: 15361): 15243, 118, | |
default (c: 118 n: 256): 166, 65, 22, 3, | |
knuth_mul (c: 118 n: 256): 161, 78, 12, 4, 1, | |
mix (c: 118 n: 256): 160, 78, 14, 4, | |
mix2 (c: 118 n: 256): 158, 80, 16, 2, | |
default (c: 118 n: 257): 160, 78, 17, 2, | |
knuth_mul (c: 118 n: 257): 164, 76, 11, 5, 0, 1, | |
mix (c: 118 n: 257): 168, 66, 20, 0, 3, | |
mix2 (c: 118 n: 257): 159, 80, 16, 2, | |
default (c: 354 n: 256): 70, 85, 52, 36, 9, 3, 1, | |
knuth_mul (c: 354 n: 256): 67, 91, 53, 29, 11, 4, 1, | |
mix (c: 354 n: 256): 60, 96, 60, 25, 12, 3, | |
mix2 (c: 354 n: 256): 61, 92, 62, 31, 6, 3, 1, | |
default (c: 354 n: 257): 62, 93, 63, 28, 6, 4, 0, 1, | |
knuth_mul (c: 354 n: 257): 68, 90, 50, 37, 8, 3, 1, | |
mix (c: 354 n: 257): 83, 86, 45, 24, 9, 3, 2, 1, 2, 0, 2, | |
mix2 (c: 354 n: 257): 62, 87, 72, 25, 7, 4, | |
default (c: 1435 n: 250675): 249243, 1429, 3, | |
knuth_mul (c: 1435 n: 250675): 249247, 1421, 7, | |
mix (c: 1435 n: 250675): 249310, 1301, 60, 2, 2, | |
mix2 (c: 1435 n: 250675): 249249, 1419, 6, 0, 1, | |
default (c: 4305 n: 250675): 246411, 4223, 41, | |
knuth_mul (c: 4305 n: 250675): 246421, 4204, 49, 1, | |
mix (c: 4305 n: 250675): 246981, 3172, 452, 53, 16, 0, 1, | |
mix2 (c: 4305 n: 250675): 246422, 4206, 45, 1, 0, 0, 1, | |
default (c: 1435 n: 250681): 249252, 1423, 6, | |
knuth_mul (c: 1435 n: 250681): 249252, 1423, 6, | |
mix (c: 1435 n: 250681): 249316, 1301, 60, 2, 2, | |
mix2 (c: 1435 n: 250681): 249257, 1415, 8, 0, 1, | |
default (c: 1435 n: 1024): 256, 352, 235, 133, 28, 19, 0, 1, | |
knuth_mul (c: 1435 n: 1024): 254, 359, 237, 110, 48, 16, | |
mix (c: 1435 n: 1024): 254, 350, 246, 121, 39, 12, 1, 0, 1, | |
mix2 (c: 1435 n: 1024): 253, 355, 246, 118, 30, 19, 2, 1, | |
default (c: 1435 n: 1031): 250, 350, 268, 115, 41, 4, 1, 2, | |
knuth_mul (c: 1435 n: 1031): 253, 377, 228, 110, 48, 12, 2, 0, 1, | |
mix (c: 1435 n: 1031): 253, 371, 232, 121, 37, 15, 1, 0, 1, | |
mix2 (c: 1435 n: 1031): 254, 355, 264, 98, 46, 10, 4, | |
default (c: 4305 n: 1024): 19, 59, 116, 207, 206, 182, 102, 68, 36, 17, 5, 4, 3, | |
knuth_mul (c: 4305 n: 1024): 18, 66, 137, 189, 200, 158, 118, 66, 35, 20, 11, 4, 2, | |
mix (c: 4305 n: 1024): 11, 59, 151, 189, 203, 154, 124, 64, 36, 21, 6, 6, | |
mix2 (c: 4305 n: 1024): 25, 62, 143, 181, 200, 143, 116, 81, 41, 20, 5, 6, 1, | |
default (c: 4305 n: 1031): 17, 60, 134, 209, 184, 186, 120, 66, 22, 16, 8, 7, 1, 1, | |
knuth_mul (c: 4305 n: 1031): 17, 61, 128, 213, 200, 176, 103, 65, 33, 23, 9, 2, 0, 1, | |
mix (c: 4305 n: 1031): 15, 73, 143, 170, 203, 178, 116, 70, 37, 17, 5, 1, 1, 2, | |
mix2 (c: 4305 n: 1031): 20, 87, 130, 178, 192, 170, 113, 64, 36, 24, 12, 4, 1, | |
default (c: 5932 n: 609638): 603744, 5856, 38, | |
knuth_mul (c: 5932 n: 609638): 603757, 5830, 51, | |
mix (c: 5932 n: 609638): 604378, 4657, 539, 59, 5, | |
mix2 (c: 5932 n: 609638): 603743, 5859, 35, 1, | |
default (c: 17796 n: 609638): 592187, 17112, 333, 6, | |
knuth_mul (c: 17796 n: 609638): 592213, 17055, 369, 1, | |
mix (c: 17796 n: 609638): 596984, 8860, 2815, 730, 172, 52, 16, 5, 1, 1, 2, | |
mix2 (c: 17796 n: 609638): 592180, 17133, 319, 5, 0, 0, 0, 0, 0, 0, 1, | |
default (c: 5932 n: 609641): 603738, 5874, 29, | |
knuth_mul (c: 5932 n: 609641): 603747, 5856, 38, | |
mix (c: 5932 n: 609641): 604381, 4657, 539, 59, 5, | |
mix2 (c: 5932 n: 609641): 603746, 5859, 35, 1, | |
default (c: 5932 n: 2048): 108, 312, 489, 486, 317, 188, 86, 40, 18, 4, | |
knuth_mul (c: 5932 n: 2048): 111, 329, 486, 433, 351, 193, 82, 40, 17, 5, 1, | |
mix (c: 5932 n: 2048): 114, 328, 475, 442, 344, 190, 103, 37, 11, 3, 1, | |
mix2 (c: 5932 n: 2048): 110, 307, 484, 489, 327, 185, 93, 33, 15, 2, 3, | |
default (c: 5932 n: 2053): 118, 326, 486, 429, 359, 180, 100, 37, 14, 3, 0, 0, 0, 1, | |
knuth_mul (c: 5932 n: 2053): 113, 336, 477, 459, 317, 186, 112, 35, 12, 6, | |
mix (c: 5932 n: 2053): 112, 370, 449, 440, 329, 195, 86, 50, 15, 5, 1, 0, 1, | |
mix2 (c: 5932 n: 2053): 119, 325, 471, 480, 306, 206, 83, 44, 10, 6, 2, 1, | |
default (c: 17796 n: 2048): 0, 1, 12, 49, 81, 119, 210, 250, 290, 285, 244, 181, 116, 89, 59, 30, 10, 12, 7, 0, 2, 0, 0, 0, 1, | |
knuth_mul (c: 17796 n: 2048): 1, 1, 15, 39, 90, 145, 221, 251, 252, 263, 240, 175, 135, 92, 49, 38, 22, 4, 10, 3, 1, 0, 0, 1, | |
mix (c: 17796 n: 2048): 1, 3, 14, 33, 81, 147, 207, 224, 294, 287, 237, 194, 118, 80, 64, 39, 14, 3, 7, 0, 1, | |
mix2 (c: 17796 n: 2048): 0, 3, 11, 32, 87, 152, 205, 244, 287, 272, 228, 189, 129, 90, 52, 31, 16, 11, 5, 1, 2, 0, 0, 1, | |
default (c: 17796 n: 2053): 0, 4, 13, 37, 91, 135, 214, 267, 272, 261, 227, 191, 114, 97, 67, 29, 19, 11, 3, 0, 1, | |
knuth_mul (c: 17796 n: 2053): 1, 4, 15, 39, 82, 137, 243, 237, 272, 283, 208, 189, 120, 84, 60, 41, 19, 7, 6, 5, 1, | |
mix (c: 17796 n: 2053): 0, 8, 20, 41, 91, 168, 216, 229, 254, 263, 197, 200, 134, 90, 62, 39, 15, 9, 5, 6, 3, 2, 0, 1, | |
mix2 (c: 17796 n: 2053): 0, 1, 13, 45, 80, 128, 213, 268, 266, 301, 221, 186, 134, 79, 54, 27, 16, 11, 7, 1, 1, 0, 0, 1, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment