summaryrefslogtreecommitdiffstats
path: root/tests/exp/ip-hash.c
blob: 8bb2d488fc424e418516ad495b2991c0648cc0fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
 * Integer hashing tests. These functions work with 32-bit integers, so are
 * perfectly suited for IPv4 addresses. A few tests show that they may also
 * be chained for larger keys (eg: IPv6), this way :
 *   f(x[0-3]) = f(f(f(f(x[0])^x[1])^x[2])^x[3])
 *
 * See also bob jenkin's site for more info on hashing, and check perfect
 * hashing for constants (eg: header names).
 */

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <math.h>

#define NSERV   8
#define MAXLINE 1000


int counts_id[NSERV][NSERV];
uint32_t hash_id( uint32_t a)
{
	return a;
}

/* Full-avalanche integer hashing function from Thomas Wang, suitable for use
 * with a modulo. See below, worth a read !
 * http://www.concentric.net/~Ttwang/tech/inthash.htm
 *
 * See also tests performed by Bob Jenkins (says it's faster than his) :
 * http://burtleburtle.net/bob/hash/integer.html
 *
 * This function is small and fast. It does not seem as smooth as bj6 though.
 * About 0x40 bytes, 6 shifts.
 */
int counts_tw1[NSERV][NSERV];
uint32_t hash_tw1(uint32_t a)
{
	a += ~(a<<15);
	a ^=  (a>>10);
	a +=  (a<<3);
	a ^=  (a>>6);
	a += ~(a<<11);
	a ^=  (a>>16);
	return a;
}

/* Thomas Wang's mix function. The multiply is optimized away by the compiler
 * on most platforms.
 * It is about equivalent to the one above.
 */
int counts_tw2[NSERV][NSERV];
uint32_t hash_tw2(uint32_t a)
{
	a = ~a + (a << 15);
	a = a ^ (a >> 12);
	a = a + (a << 2);
	a = a ^ (a >> 4);
	a = a * 2057;
	a = a ^ (a >> 16);
	return a;
}

/* Thomas Wang's multiplicative hash function. About 0x30 bytes, and it is
 * extremely fast on recent processors with a fast multiply. However, it
 * must not be used on low bits only, as multiples of 0x00100010 only return
 * even values !
 */
int counts_tw3[NSERV][NSERV];
uint32_t hash_tw3(uint32_t a)
{
	a = (a ^ 61) ^ (a >> 16);
	a = a + (a << 3);
	a = a ^ (a >> 4);
	a = a * 0x27d4eb2d;
	a = a ^ (a >> 15);
	return a;
}


/* Full-avalanche integer hashing function from Bob Jenkins, suitable for use
 * with a modulo. It has a very smooth distribution.
 * http://burtleburtle.net/bob/hash/integer.html
 * About 0x50 bytes, 6 shifts.
 */
int counts_bj6[NSERV][NSERV];
int counts_bj6x[NSERV][NSERV];
uint32_t hash_bj6(uint32_t a)
{
	a = (a+0x7ed55d16) + (a<<12);
	a = (a^0xc761c23c) ^ (a>>19);
	a = (a+0x165667b1) + (a<<5);
	a = (a+0xd3a2646c) ^ (a<<9);
	a = (a+0xfd7046c5) + (a<<3);
	a = (a^0xb55a4f09) ^ (a>>16);
	return a;
}

/* Similar function with one more shift and no magic number. It is slightly
 * slower but provides the overall smoothest distribution.
 * About 0x40 bytes, 7 shifts.
 */
int counts_bj7[NSERV][NSERV];
int counts_bj7x[NSERV][NSERV];
uint32_t hash_bj7(uint32_t a)
{
	a -= (a<<6);
	a ^= (a>>17);
	a -= (a<<9);
	a ^= (a<<4);
	a -= (a<<3);
	a ^= (a<<10);
	a ^= (a>>15);
	return a;
}


void count_hash_results(unsigned long hash, int counts[NSERV][NSERV]) {
	int srv, nsrv;
    
	for (nsrv = 0; nsrv < NSERV; nsrv++) {
		srv = hash % (nsrv + 1);
		counts[nsrv][srv]++;
	}
}

void dump_hash_results(char *name, int counts[NSERV][NSERV]) {
	int srv, nsrv;
	double err, total_err, max_err;

	printf("%s:\n", name);
	for (nsrv = 0; nsrv < NSERV; nsrv++) {
		total_err = 0.0;
		max_err = 0.0;
		printf("%02d srv: ", nsrv+1);
		for (srv = 0; srv <= nsrv; srv++) {
			err = 100.0*(counts[nsrv][srv] - (double)counts[0][0]/(nsrv+1)) / (double)counts[0][0];
			//printf("%6d ", counts[nsrv][srv]);
			printf("% 3.1f%%%c ", err,
			       counts[nsrv][srv]?' ':'*');  /* display '*' when a server is never selected */
			err = fabs(err);
			total_err += err;
			if (err > max_err)
				max_err = err;
		}
		total_err /= (double)(nsrv+1);
		for (srv = nsrv+1; srv < NSERV; srv++)
			printf("       ");
		printf("  avg_err=%3.1f, max_err=%3.1f\n", total_err, max_err);
	}
	printf("\n");
}

int main() {
	int nr;
	unsigned int address = 0;
	unsigned int mask = ~0;

	memset(counts_id, 0, sizeof(counts_id));
	memset(counts_tw1, 0, sizeof(counts_tw1));
	memset(counts_tw2, 0, sizeof(counts_tw2));
	memset(counts_tw3, 0, sizeof(counts_tw3));
	memset(counts_bj6, 0, sizeof(counts_bj6));
	memset(counts_bj7, 0, sizeof(counts_bj7));

	address = 0x10000000;
	mask = 0xffffff00;  // user mask to apply to addresses
	for (nr = 0; nr < 0x10; nr++) {
		//address += ~nr;  // semi-random addresses.
		//address += 1;
		address += 0x00000100;
		//address += 0x11111111;
		//address += 7;
		//address += 8;
		//address += 256;
		//address += 65536;
		//address += 131072;
		//address += 0x00100010;   // this increment kills tw3 !
		count_hash_results(hash_id (address & mask), counts_id);   // 0.69s / 100M
		count_hash_results(hash_tw1(address & mask), counts_tw1);  // 1.04s / 100M
		count_hash_results(hash_tw2(address & mask), counts_tw2);  // 1.13s / 100M
		count_hash_results(hash_tw3(address & mask), counts_tw3);  // 1.01s / 100M
		count_hash_results(hash_bj6(address & mask), counts_bj6);  // 1.07s / 100M
		count_hash_results(hash_bj7(address & mask), counts_bj7);  // 1.20s / 100M
		/* adding the original address after the hash reduces the error
		 * rate in in presence of very small data sets (eg: 16 source
		 * addresses for 8 servers). In this case, bj7 is very good.
		 */
		count_hash_results(hash_bj6(address & mask)+(address&mask), counts_bj6x); // 1.07s / 100M
		count_hash_results(hash_bj7(address & mask)+(address&mask), counts_bj7x); // 1.20s / 100M
	}

	dump_hash_results("hash_id", counts_id);
	dump_hash_results("hash_tw1", counts_tw1);
	dump_hash_results("hash_tw2", counts_tw2);
	dump_hash_results("hash_tw3", counts_tw3);
	dump_hash_results("hash_bj6", counts_bj6);
	dump_hash_results("hash_bj6x", counts_bj6x);
	dump_hash_results("hash_bj7", counts_bj7);
	dump_hash_results("hash_bj7x", counts_bj7x);
	return 0;
}