四分木エントリを格納するハッシュテーブルがあります。
ハッシュ関数は次のようになります。
四分木ハッシュ
#define node_hash(a,b,c,d) \
(((int)(d))+3*(((int)(c))+3*(((int)(b))+3*((int)(a))+3)))
この操作の結果は、次のように剰余素数を使用して常に分割されることに注意してください。
h = node_hash(p->nw, p->ne, p->sw, p->se) ;
h %= hashprime ;
...
最適なハッシュとの比較
いくつかの統計分析は、このハッシュが衝突の削減に関して最適であることを示しています。バケットとエントリ
を含むハッシュテーブルが与えられます。完全ハッシュを使用した場合の衝突リスクは次のとおりです。
n = b の場合、これは 37% の衝突リスクを意味します。 b
n
(n - b * (1 - power((b-1)/b,n)))) * 100 / n
いくつかのテストでは、上記のハッシュが (ハッシュテーブルのすべての塗りつぶしレベルに対して) 標準と非常にうまく一致していることが示されています。
実行時間 実行時間
は、次の値に大きく依存します。hashprime
タイミング (1000 回の実行で最適) は次のとおりです。
hashprime CPU-cycles per run
--------------------------------
4049 56
16217 68
64871 127 <-- whoooh
最適な衝突リスクを維持しながら、これを改善する方法はありますか?
モジュラス演算を最適化する(ループ外の「マジック」ナンバーコンピューターを使用した乗算に置き換える)。
ハッシュ関数を他のハッシュ関数に置き換えます。
背景
以下のアセンブリが生成されます。
//--------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
[編集]
私は次の事実で何かをすることができるかもしれません:
C = A % B
C = A – B * (A / B)
は、整数の除算がその逆数による乗算と同じであるという事実を使用することと同等です。
整数除算の場合、逆数は魔法の数であること
に注意してください。 http://www.hackersdelight.org/magic.htm
C コードはこちら: http://web.archive.org/web/20070713211039/ http://hackersdelight.org/HDcode/magic.cC = A - B * (A * rB)
[FNV ハッシュ]
参照: 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
32 ビットに切り捨てられた 4 つのポインター = 16 バイトの場合、FNV ハッシュには27 サイクルかかります (手作りのアセンブリ)
残念ながら、これにより 37% になるはずの場所で 81% のハッシュ衝突が発生します。
15 回の乗算をすべて実行するには、179 サイクルかかります。