diff --git a/src/builtins/search.c b/src/builtins/search.c index 3ca88d2e..2c7a7563 100644 --- a/src/builtins/search.c +++ b/src/builtins/search.c @@ -24,6 +24,10 @@ #include "../utils/talloc.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]; #if SINGELI extern RangeFn* const simd_getRangeRaw; @@ -345,6 +349,13 @@ B memberOf_c2(B t, B w, B x) { TABLE(x, w, i8, 0, 1) 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); diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 93de7f93..c238d595 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -43,8 +43,8 @@ extern B mul_c2(B, B, B); extern B scan_add_bool(B x, 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> 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< { 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} + } + } +} diff --git a/src/singeli/src/search.singeli b/src/singeli/src/search.singeli index 21ef5e2d..e59f3adf 100644 --- a/src/singeli/src/search.singeli +++ b/src/singeli/src/search.singeli @@ -1,6 +1,7 @@ include './base' include './mask' include './vecfold' +include './hashmap' def findFirst{C, M, F, ...v1} = { 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}}} + + +# 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 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> 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< { 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} - } - } -} +include './hashmap' def names = tup{'memberOf', 'count', 'indexOf'} def prims = tup{'∊', '⊒', '⊐' } @@ -108,8 +46,8 @@ fn selfhashtab{T, name}(rp:*rty{name}, xp:*T, n:usz) = { x0 := hash_val{xp->0} rp <-{0} res0 def aux = prim!='∊' - def {halloc,{hash,...vals}} = hash_alloc{ - sz, msz, ext, merge{tup{T}, aux**u32}, merge{tup{x0}, aux**0} + def {{hash,...vals}, hash_free} = hash_alloc{ + sz, msz, ext, tup{T, ...aux**u32}, tup{x0, ...aux**0} } def {output, write_res} = match (prim) { @@ -160,8 +98,8 @@ fn selfhashtab{T, name}(rp:*rty{name}, xp:*T, n:usz) = { } } setlabel{break} - tfree{halloc} - output{i == n} # Whether it finished + hash_free{} + output{i == n} # Whether it finished, and unique count if ⊐ } def exp_hash{T, name} = {