Robin Hood hash table for 32-bit membership
This commit is contained in:
parent
cb8be206be
commit
ed03720441
@ -24,6 +24,10 @@
|
|||||||
#include "../utils/talloc.h"
|
#include "../utils/talloc.h"
|
||||||
#include "../utils/calls.h"
|
#include "../utils/calls.h"
|
||||||
|
|
||||||
|
// From selfsearch.c
|
||||||
|
extern NOINLINE void memset32(u32* p, u32 v, usz l);
|
||||||
|
extern NOINLINE void memset64(u64* p, u64 v, usz l);
|
||||||
|
|
||||||
RangeFn getRange_fns[el_f64+1];
|
RangeFn getRange_fns[el_f64+1];
|
||||||
#if SINGELI
|
#if SINGELI
|
||||||
extern RangeFn* const simd_getRangeRaw;
|
extern RangeFn* const simd_getRangeRaw;
|
||||||
@ -345,6 +349,13 @@ B memberOf_c2(B t, B w, B x) {
|
|||||||
TABLE(x, w, i8, 0, 1)
|
TABLE(x, w, i8, 0, 1)
|
||||||
return taga(cpyBitArr(r));
|
return taga(cpyBitArr(r));
|
||||||
}
|
}
|
||||||
|
if (we==el_i32 && xe==el_i32) {
|
||||||
|
i8* rp; B r = m_i8arrc(&rp, w);
|
||||||
|
if (memberOf_c2_hash32(rp, tyany_ptr(x), xia, tyany_ptr(w), wia)) {
|
||||||
|
decG(w); decG(x); return taga(cpyBitArr(r));
|
||||||
|
}
|
||||||
|
decG(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
H_Sb* set = m_Sb(64);
|
H_Sb* set = m_Sb(64);
|
||||||
|
|||||||
@ -43,8 +43,8 @@ extern B mul_c2(B, B, B);
|
|||||||
extern B scan_add_bool(B x, u64 ia);
|
extern B scan_add_bool(B x, u64 ia);
|
||||||
extern B scan_max_num(B x, u8 xe, u64 ia);
|
extern B scan_max_num(B x, u8 xe, u64 ia);
|
||||||
|
|
||||||
static NOINLINE void memset32(u32* p, u32 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
|
extern NOINLINE void memset32(u32* p, u32 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
|
||||||
static NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
|
extern NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i<l; i++) p[i]=v; }
|
||||||
|
|
||||||
#if SINGELI
|
#if SINGELI
|
||||||
#define SINGELI_FILE selfsearch
|
#define SINGELI_FILE selfsearch
|
||||||
|
|||||||
64
src/singeli/src/hashmap.singeli
Normal file
64
src/singeli/src/hashmap.singeli
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
local include 'skin/cext'
|
||||||
|
local include './cbqnDefs' # talloc/tfree
|
||||||
|
|
||||||
|
# Defined in C
|
||||||
|
def memset{p:pT, v, l} = {
|
||||||
|
emit{void, merge{'memset',fmtnat{elwidth{pT}}}, p, v, l}
|
||||||
|
}
|
||||||
|
|
||||||
|
# These hashes are stored in tables and must be invertible!
|
||||||
|
# Murmur3
|
||||||
|
def hash_val{x0:u32} = {
|
||||||
|
x := x0
|
||||||
|
x ^= x >> 16; x *= 0x85ebca6b
|
||||||
|
x ^= x >> 13; x *= 0xc2b2ae35
|
||||||
|
x ^= x >> 16; x
|
||||||
|
}
|
||||||
|
def hash_val{x0:u64} = {
|
||||||
|
x := x0
|
||||||
|
x ^= x >> 33; x *= 0xff51afd7ed558ccd
|
||||||
|
x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53
|
||||||
|
x ^= x >> 33; x
|
||||||
|
}
|
||||||
|
# CRC32
|
||||||
|
def hash_val{x:u32 & hasarch{'SSE4.2'}} = {
|
||||||
|
emit{u32, '_mm_crc32_u32', 0x973afb51, x}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Allocate and initialize resizing hash table
|
||||||
|
# Initial size sz+ext and maximum size msz+ext
|
||||||
|
# One region for each type in Ts initialized with value from v0s
|
||||||
|
def hash_alloc{sz, msz, ext, Ts, v0s} = {
|
||||||
|
def ws = each{width,Ts}
|
||||||
|
each{assert, slice{ws,0,-1} >= slice{ws,1}} # Doesn't do alignment
|
||||||
|
def add{}=0; def add{a,...r} = a+add{...r}
|
||||||
|
halloc := talloc{u8, (msz+ext)*add{...ws/8}}
|
||||||
|
szo := msz-sz # Beginning of allocation to initial table
|
||||||
|
sze := sz+ext # Initial table to end of allocation
|
||||||
|
def pe{{}} = halloc; def pl{{}} = tup{}
|
||||||
|
def pe{{..._, p}} = p+sze # Next unallocated space given pointers so far
|
||||||
|
def pl{{...R, T}} = { def ps=pl{R}; tup{...ps, *T~~pe{ps}+szo} }
|
||||||
|
ptrs := pl{Ts}
|
||||||
|
each{memset{., ., sze}, ptrs, v0s}
|
||||||
|
tup{ptrs, {}=>tfree{halloc}}
|
||||||
|
}
|
||||||
|
|
||||||
|
def hash_resize{hash,vals, x0,sh,sz,ext,cc, m} = {
|
||||||
|
dif := sz*((1<<m)-1)
|
||||||
|
sh -= m; sz <<= m
|
||||||
|
hash -= dif
|
||||||
|
j:usz = 0
|
||||||
|
cc = 0
|
||||||
|
memset{hash, x0, dif}
|
||||||
|
each{{val} => { val -= dif; memset{val, 0, dif} }, vals}
|
||||||
|
@for (j from dif to sz + ext) {
|
||||||
|
h := hash->j
|
||||||
|
if (h != x0) {
|
||||||
|
hash <-{j} x0
|
||||||
|
k0 := h>>sh; k := k0; while (hash->k!=x0) ++k
|
||||||
|
cc += cast_i{usz, k-k0}
|
||||||
|
hash <-{k} h
|
||||||
|
each{{val} => { v := val->j; val <-{j} 0; val <-{k} v }, vals}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
include './base'
|
include './base'
|
||||||
include './mask'
|
include './mask'
|
||||||
include './vecfold'
|
include './vecfold'
|
||||||
|
include './hashmap'
|
||||||
|
|
||||||
def findFirst{C, M, F, ...v1} = {
|
def findFirst{C, M, F, ...v1} = {
|
||||||
def exit = makelabel{}
|
def exit = makelabel{}
|
||||||
@ -329,3 +330,73 @@ fn getRange{E}(x0:*void, res:*i64, n:u64) : u1 = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exportT{'simd_getRangeRaw', each{getRange, tup{i8,i16,i32,f64}}}
|
exportT{'simd_getRangeRaw', each{getRange, tup{i8,i16,i32,f64}}}
|
||||||
|
|
||||||
|
|
||||||
|
# Hash tables
|
||||||
|
fn hashtab_memberOf{T}(rp:*i8, ip:*T, m:usz, fp:*T, n:usz) = {
|
||||||
|
def wt = width{T}
|
||||||
|
def stop = m
|
||||||
|
|
||||||
|
log := clzc{m}
|
||||||
|
msl := clzc{m} + 2
|
||||||
|
sh := wt - tern{msl<14, msl, 12+(msl&1)} # Shift to fit to table
|
||||||
|
sz := usz~~1 << (wt - sh) # Initial size
|
||||||
|
msz := usz~~1 << msl # Max sz
|
||||||
|
b:usz = 64 # Block size
|
||||||
|
|
||||||
|
# Filling e slots past the end requires e*(e+1)/2 collisions, so
|
||||||
|
# m entries with <2 each can fill <sqrt(4*m)
|
||||||
|
ext := tern{m<=b, m, b + (usz~~1 << (log/2 + 1))}
|
||||||
|
maxh := T~~maxvalue{T}
|
||||||
|
def {{hash,...vals}, hash_free} = hash_alloc{
|
||||||
|
sz, msz, ext, tup{T}, tup{maxh}
|
||||||
|
}
|
||||||
|
|
||||||
|
def lookup=makelabel{}; def abort=makelabel{}; def dup=makelabel{}
|
||||||
|
cc:usz = 0 # Collision counter
|
||||||
|
has_maxh:u1 = 0
|
||||||
|
i:usz=0; while (1) {
|
||||||
|
e := tern{m-i>b, i+b, m}
|
||||||
|
while (i < e) {
|
||||||
|
h := hash_val{load{ip,i}}; j := h>>sh
|
||||||
|
k := load{hash, j}
|
||||||
|
# Robin Hood insertion
|
||||||
|
if (k == maxh) {
|
||||||
|
has_maxh |= h==maxh
|
||||||
|
store{hash, j, h}
|
||||||
|
} else if (k != h) {
|
||||||
|
j0 := j; ji := j # Save value; insertion point
|
||||||
|
do {
|
||||||
|
++j; kn := load{hash,j}; if (kn==h) goto{dup}
|
||||||
|
def c = promote{T, h >= k}
|
||||||
|
store{hash, j-c, k}
|
||||||
|
ji += c
|
||||||
|
k = kn
|
||||||
|
} while (k != maxh)
|
||||||
|
store{hash, ji, h}
|
||||||
|
cc += cast_i{usz, j-j0}
|
||||||
|
setlabel{dup}
|
||||||
|
}
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
if (i == m) goto{lookup}
|
||||||
|
# Check collision counter and possibly resize (needs tuning)
|
||||||
|
if (cc >= stop and sz == msz) goto{abort}
|
||||||
|
if (16*cc >= stop and sz < msz) {
|
||||||
|
hash_resize{hash,vals, maxh,sh,sz,ext,cc, 2} # Resize hash, factor of 4
|
||||||
|
if (cc >= stop) goto{abort}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setlabel{lookup}
|
||||||
|
end := maxh>>sh + promote{T,ext} # Unreachable
|
||||||
|
if (has_maxh) { end=maxh>>sh; while (load{hash,end}!=maxh) ++end }
|
||||||
|
@for (rp, fp over n) {
|
||||||
|
h := hash_val{fp}; j := h>>sh
|
||||||
|
k:=undefined{T}; while ((k=load{hash,j}) < h) ++j
|
||||||
|
rp = promote{i8, (k==h) & (j<end)}
|
||||||
|
}
|
||||||
|
setlabel{abort}
|
||||||
|
hash_free{}
|
||||||
|
i == m # Whether it finished
|
||||||
|
}
|
||||||
|
export{'memberOf_c2_hash32', hashtab_memberOf{u32}}
|
||||||
|
|||||||
@ -1,68 +1,6 @@
|
|||||||
include './base'
|
include './base'
|
||||||
include './cbqnDefs'
|
|
||||||
local include 'skin/cext'
|
local include 'skin/cext'
|
||||||
|
include './hashmap'
|
||||||
# Defined in C
|
|
||||||
def memset{p:pT, v, l} = {
|
|
||||||
emit{void, merge{'memset',fmtnat{elwidth{pT}}}, p, v, l}
|
|
||||||
}
|
|
||||||
|
|
||||||
# These hashes are stored in tables and must be invertible!
|
|
||||||
# Murmur3
|
|
||||||
def hash_val{x0:u32} = {
|
|
||||||
x := x0
|
|
||||||
x ^= x >> 16; x *= 0x85ebca6b
|
|
||||||
x ^= x >> 13; x *= 0xc2b2ae35
|
|
||||||
x ^= x >> 16; x
|
|
||||||
}
|
|
||||||
def hash_val{x0:u64} = {
|
|
||||||
x := x0
|
|
||||||
x ^= x >> 33; x *= 0xff51afd7ed558ccd
|
|
||||||
x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53
|
|
||||||
x ^= x >> 33; x
|
|
||||||
}
|
|
||||||
# CRC32
|
|
||||||
def hash_val{x:u32 & hasarch{'SSE4.2'}} = {
|
|
||||||
emit{u32, '_mm_crc32_u32', 0x973afb51, x}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allocate and initialize resizing hash table
|
|
||||||
# Initial size sz+ext and maximum size msz+ext
|
|
||||||
# One region for each type in Ts initialized with value from v0s
|
|
||||||
def hash_alloc{sz, msz, ext, Ts, v0s} = {
|
|
||||||
def ws = each{width,Ts}
|
|
||||||
each{assert, slice{ws,0,-1} >= slice{ws,1}} # Doesn't do alignment
|
|
||||||
def add{}=0; def add{a,...r} = a+add{...r}
|
|
||||||
halloc := talloc{u8, (msz+ext)*add{...ws/8}}
|
|
||||||
szo := msz-sz # Beginning of allocation to initial table
|
|
||||||
sze := sz+ext # Initial table to end of allocation
|
|
||||||
def pe{{}} = halloc; def pl{{}} = tup{}
|
|
||||||
def pe{{..._, p}} = p+sze # Next unallocated space given pointers so far
|
|
||||||
def pl{{...R, T}} = { def ps=pl{R}; tup{...ps, *T~~pe{ps}+szo} }
|
|
||||||
ptrs := pl{Ts}
|
|
||||||
each{memset{., ., sze}, ptrs, v0s}
|
|
||||||
tup{halloc, ptrs}
|
|
||||||
}
|
|
||||||
|
|
||||||
def hash_resize{hash,vals, x0,sh,sz,ext,cc, m} = {
|
|
||||||
dif := sz*((1<<m)-1)
|
|
||||||
sh -= m; sz <<= m
|
|
||||||
hash -= dif
|
|
||||||
j:usz = 0
|
|
||||||
cc = 0
|
|
||||||
memset{hash, x0, dif}
|
|
||||||
each{{val} => { val -= dif; memset{val, 0, dif} }, vals}
|
|
||||||
@for (j from dif to sz + ext) {
|
|
||||||
h := hash->j
|
|
||||||
if (h != x0) {
|
|
||||||
hash <-{j} x0
|
|
||||||
k0 := h>>sh; k := k0; while (hash->k!=x0) ++k
|
|
||||||
cc += cast_i{usz, k-k0}
|
|
||||||
hash <-{k} h
|
|
||||||
each{{val} => { v := val->j; val <-{j} 0; val <-{k} v }, vals}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def names = tup{'memberOf', 'count', 'indexOf'}
|
def names = tup{'memberOf', 'count', 'indexOf'}
|
||||||
def prims = tup{'∊', '⊒', '⊐' }
|
def prims = tup{'∊', '⊒', '⊐' }
|
||||||
@ -108,8 +46,8 @@ fn selfhashtab{T, name}(rp:*rty{name}, xp:*T, n:usz) = {
|
|||||||
x0 := hash_val{xp->0}
|
x0 := hash_val{xp->0}
|
||||||
rp <-{0} res0
|
rp <-{0} res0
|
||||||
def aux = prim!='∊'
|
def aux = prim!='∊'
|
||||||
def {halloc,{hash,...vals}} = hash_alloc{
|
def {{hash,...vals}, hash_free} = hash_alloc{
|
||||||
sz, msz, ext, merge{tup{T}, aux**u32}, merge{tup{x0}, aux**0}
|
sz, msz, ext, tup{T, ...aux**u32}, tup{x0, ...aux**0}
|
||||||
}
|
}
|
||||||
|
|
||||||
def {output, write_res} = match (prim) {
|
def {output, write_res} = match (prim) {
|
||||||
@ -160,8 +98,8 @@ fn selfhashtab{T, name}(rp:*rty{name}, xp:*T, n:usz) = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setlabel{break}
|
setlabel{break}
|
||||||
tfree{halloc}
|
hash_free{}
|
||||||
output{i == n} # Whether it finished
|
output{i == n} # Whether it finished, and unique count if ⊐
|
||||||
}
|
}
|
||||||
|
|
||||||
def exp_hash{T, name} = {
|
def exp_hash{T, name} = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user