375050个字符窜(小的几个字符大的几百个字符),进行6108120(很多重复)次操作,用不同哈希函数的哈希表速度(std::unordered_set)对比,单位:毫秒。
测试环境:系统WinXP,g++4.6.3(-O3),Intel I3 2120,2G内存
哈希 | RS | JS | PJW | ELF | BKDR | SDBM | DJB | DEK | BP | FNV | AP | BMF |
插入 | 1529 | 1783.12 | 2594.24 | 2612.27 | 1534.49 | 1586.76 | 1548.43 | 1447.74 | 无穷大 | 1591.83 | 2078.48 | 1277.42 |
查找 | 1316.3 | 1506.2 | 2162.45 | 2178.9 | 1311.76 | 1308.41 | 1307.99 | 1257.59 | 无穷大 | 1311.85 | 1719.16 | 1219.48 |
删除 | 1974.04 | 2244.73 | 3447.51 | 3466.72 | 1923.81 | 1911.25 | 1961.45 | 1842.1 | 无穷大 | 1900.16 | 2600.93 | 1384.3 |
结论:相对复杂的Bloom filter以微弱优势胜出,简单的DEK哈希值得称赞。
除了上面12个哈希函数外,下面两个也许更好用,看上去比上面那些都复杂。
murmur哈希。其中unint32_t就是32位无符号整型,大概相当于unsigned long。
1 // subject to change
2 #define ROT32(x, y) ((x << y) | (x >> (32 - y)))
3 uint32_t murmur3(const char *key, uint32_t len, uint32_t seed)
4 {
5 static const uint32_t c1 = 0xcc9e2d51;
6 static const uint32_t c2 = 0x1b873593;
7 static const uint32_t r1 = 15;
8 static const uint32_t r2 = 13;
9 static const uint32_t m = 5;
10 static const uint32_t n = 0xe6546b64;
11
12 uint32_t val = seed;
13
14 const int nblocks = len >> 2;
15 const uint32_t *blocks = (const uint32_t*) key;
16 int i;
17 uint32_t k;
18 for(i = 0; i < nblocks; i++)
19 {
20 k = blocks[i];
21 k *= c1;
22 k = ROT32(k, r1);
23 k *= c2;
24
25 val ^= k;
26 val = ROT32(val, r2) * m + n;
27 }
28
29 const uint8_t *tail = (const uint8_t*) (key + nblocks * 4);
30 uint32_t k1 = 0;
31
32 switch(len & 3)
33 {
34 case 3:
35 k1 ^= tail[2] << 16;
36 case 2:
37 k1 ^= tail[1] << 8;
38 case 1:
39 k1 ^= tail[0];
40
41 k1 *= c1;
42 k1 = ROT32(k1, r1);
43 k1 *= c2;
44 val ^= k1;
45 }
46
47 val ^= len;
48 val ^= (val >> 16);
49 val *= 0x85ebca6b;
50 val ^= (val >> 13);
51 val *= 0xc2b2ae35;
52 val ^= (val >> 16);
53
54 return val;
55 }
crapwow不为多数人所知,但是这个哈希函数的效果比murmur还要稍微好一点。除此之外还有Google的CityHash,特别复杂,据说比murmur也好一点,没有和crapwow对比过。
1 uint32_t crapwow(const char* key, uint32_t len, uint32_t seed)
2 {
3 #if !defined(__LP64__) && (defined __i386__) && (defined __GNUG__)
4 // esi = k, ebx = h
5 uint32_t val;
6 asm(
7 "leal 0x5052acdb(%%ecx,%%esi), %%esi\n"
8 "movl %%ecx, %%ebx\n"
9 "cmpl $8, %%ecx\n"
10 "jb DW%=\n"
11 "QW%=:\n"
12 "movl $0x5052acdb, %%eax\n"
13 "mull (%%edi)\n"
14 "addl $-8, %%ecx\n"
15 "xorl %%eax, %%ebx\n"
16 "xorl %%edx, %%esi\n"
17 "movl $0x57559429, %%eax\n"
18 "mull 4(%%edi)\n"
19 "xorl %%eax, %%esi\n"
20 "xorl %%edx, %%ebx\n"
21 "addl $8, %%edi\n"
22 "cmpl $8, %%ecx\n"
23 "jae QW%=\n"
24 "DW%=:\n"
25 "cmpl $4, %%ecx\n"
26 "jb B%=\n"
27 "movl $0x5052acdb, %%eax\n"
28 "mull (%%edi)\n"
29 "addl $4, %%edi\n"
30 "xorl %%eax, %%ebx\n"
31 "addl $-4, %%ecx\n"
32 "xorl %%edx, %%esi\n"
33 "B%=:\n"
34 "testl %%ecx, %%ecx\n"
35 "jz F%=\n"
36 "shll $3, %%ecx\n"
37 "movl $1, %%edx\n"
38 "movl $0x57559429, %%eax\n"
39 "shll %%cl, %%edx\n"
40 "addl $-1, %%edx\n"
41 "andl (%%edi), %%edx\n"
42 "mull %%edx\n"
43 "xorl %%eax, %%esi\n"
44 "xorl %%edx, %%ebx\n"
45 "F%=:\n"
46 "leal 0x5052acdb(%%esi), %%edx\n"
47 "xorl %%ebx, %%edx\n"
48 "movl $0x5052acdb, %%eax\n"
49 "mull %%edx\n"
50 "xorl %%ebx, %%eax\n"
51 "xorl %%edx, %%esi\n"
52 "xorl %%esi, %%eax\n"
53 : "=a"(val), "=c"(len), "=S"(len), "=D"(key)
54 : "c"(len), "S"(seed), "D"(key)
55 : "%ebx", "%edx", "cc"
56 );
57 return val;
58 #else
59 #define CWFOLD(a, b, lo, hi) \
60 {\
61 p = (uint32_t)(a) * (uint64_t)(b);\
62 lo ^= (uint32_t)p;\
63 hi ^= (uint32_t)(p >> 32);\
64 }
65
66 #define CWMIXA(in) { CWFOLD(in, m, k, h); }
67 #define CWMIXB(in) { CWFOLD(in, n, h, k); }
68
69 const uint32_t m = 0x57559429;
70 const uint32_t n = 0x5052acdb;
71 const uint32_t* key4 = (const uint32_t*)key;
72 uint32_t h = len;
73 uint32_t k = len + seed + n;
74 uint64_t p;
75
76 while(len >= 8)
77 {
78 CWMIXB(key4[0])
79 CWMIXA(key4[1])
80 key4 += 2;
81 len -= 8;
82 }
83
84 if(len >= 4)
85 {
86 CWMIXB(key4[0])
87 key4 += 1;
88 len -= 4;
89 }
90
91 if(0 != len)
92 {
93 CWMIXA(key4[0] & ((1 << (len * 8)) - 1 ))
94 }
95
96 CWMIXB(h ^ (k + n))
97 return k ^ h;
98 #endif
99 }