I have a hash table in which quadtree entries are stored.
The hash function looks like this:
Quadtree hash
Note that the result of this operation is always reset using a simple simple module:
h = node_hash(p->nw, p->ne, p->sw, p->se) ; h %= hashprime ; ...
Comparison with Optimal Hash
Some statistical analysis shows that this hash is optimal in terms of collision reduction. Given a hash table with b and n buckets. The risk of collision using an ideal hash:
(n - b * (1 - power((b-1)/b,n)))) * 100 / n
When n = b, this means a collision risk of 37%.
Some tests show that the above hash strings work very well with the norm (for all levels of filling the hash table).
Working hours
Runtime is highly dependent on hashprime
Timing (best of 1000 starts):
hashprime CPU-cycles per run -------------------------------- 4049 56 16217 68 64871 127 <-- whoooh
Is there a way to improve this while maintaining optimal collision risk?
Or by optimizing the operation of the module (replacing it with multiplication using the "magic" numbers of the computer outside the loop).
Replacing a hash function with another hash function.
Background
The following assembly is produced:
//--------h = node_hash(p->nw, p->ne, p->sw, p->se) ; mov eax,[rcx+node.nw] <<+ lea eax,[eax+eax*2+3] | add eax,[rcx+node.ne] | lea eax,[eax+eax*2] +- takes +/- 12 cycles add eax,[rcx+node.sw] | lea eax,[eax+eax*2] | add eax,[rcx+node.se] <<+ //--------h %= hashprime ; mov esi,[hashprime] xor edx,edx div esi mov rax,rdx <<--- takes all the rest
[EDIT]
I can do something with the fact that:
C = A % B equivalent to C = A – B * (A / B)
Using the fact that integer division is the same as multiplying by its inverse.
Thus, the transformation of the formula into C = A - B * (A * rB)
Note that for integer division the inverse is magic number, see http://www.hackersdelight.org/magic.htm
C here: http://web.archive.org/web/20070713211039/http://hackersdelight.org/HDcode/magic.c
[FNV hashes]
See: http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1a
hash = offset_basis for each byte to be hashed hash = hash xor octet_of_data hash = hash * FNV_prime (for 32 bits = 16777619) return hash
For 4 pointers truncated to 32 bits = 16 bytes, the FNV hash takes 27 cycles (manual assembly)
Unfortunately, this leads to hash collisions of 81%, where 37% should be.
Performing the full 15 multiplications takes 179 cycles.