From f1485ca98cf5ae81d716683f3b7f5e0c1024844a Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Wed, 8 Nov 2023 21:07:03 -0500 Subject: [PATCH] Turn hash function definition structure inside-out --- src/singeli/src/search.singeli | 239 +++++++++++++++++---------------- 1 file changed, 122 insertions(+), 117 deletions(-) diff --git a/src/singeli/src/search.singeli b/src/singeli/src/search.singeli index 66fe18f4..cb065b20 100644 --- a/src/singeli/src/search.singeli +++ b/src/singeli/src/search.singeli @@ -364,120 +364,46 @@ fn hashtab{T, name}(rp:*rty{name}, iv:*void, m:usz, fv:*void, n:usz, links:ity{n } def {hash,...vals} = tabs - def {set_tab, set_maxh, fixup, get_res} = match (prim) { - {('∊')} => { - has_maxh:u1 = 0 - def set{j,je,kr,dup} = { setlabel{dup} } - def setm{found, i} = has_maxh |= found - end := maxh>>sh # Clip trailing maxh if it shouldn't be in the table - def usem{} = { - if (swap) { - swap_sides{}; i=m # i==m return value is kind of dumb - @for (ip over m) { # Remove values in ip from hash - h := hash_val{ip}; j := h>>sh - def shortcut = makelabel{} - if (h == maxh) { - if (uniq==0) goto{shortcut} - has_maxh = 0 - } else { - k := load{hash,j} - if (k <= h) { - while (k < h) { ++j; k = load{hash,j} } - if (k == h) { - --uniq - if (uniq==0 and not has_maxh) { - setlabel{shortcut} - @for (rp over n) rp = 1 - goto{abort} - } - hash_remove{j,h} - } - } - } - } + def abort = makelabel{} + i:usz = 0 # Saved to determine if hashing finished + def insert_all{set_tab, set_maxh, dup, ...uniq} = { + cc:usz = 0 # Collision counter + def insert_finish = makelabel{} + while (1) { + e := tern{m-i>b, i+b, m} + while (i < e) { + def ii = if (prim!='⊒') i else m-i-1 + h := hash_val{load{ip,ii}}; j := h>>sh + set_maxh{h==maxh, i} + kv := each{load{.,j}, tabs}; def {k,...kr} = kv + # Robin Hood insertion + j0 := j; je := j # Save value; end of chain (insert at j) + if (k != maxh) { + if (k == h) goto{dup} + do { + ++je; knv := each{load{.,je}, tabs}; def {kn,..._} = knv + def c = promote{T, h >= k} + j += c + if (kn == h) goto{dup} + each{store{.,je-c,.}, tabs, kv} + each{=, kv, knv} + } while (k != maxh) + cc += cast_i{usz, je-j0} } - try_vec_memb{T, hash, sz, sh, maxh, has_maxh, swap, rp, fp, n, abort} - if (has_maxh) { while (load{hash,end}!=maxh) ++end } - else end += promote{T,ext} # Unreachable + each{{u} => { u += promote{usz,h!=maxh} }, uniq} + store{hash, j, h} + set_tab{j, je, kr} + ++i + } + if (i == m) goto{insert_finish} + # 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{cc, 2} # Factor of 4 + if (cc >= stop) goto{abort} } - def get{found, j} = promote{i8, swap ^ (found & (j { - def {inds} = vals - has_maxh:u1 = 0 - ind_maxh:u32 = 0 - def set{j,je,kr,dup} = { - store{inds, j, m-i} # So it can be cleared with one &- in get{} - setlabel{dup} - } - def setm{found, i} = { - ind_maxh |= i &- (found&~has_maxh) - has_maxh |= found - } - def usem{} = { - if (has_maxh) { - end := maxh>>sh; while (load{hash,end}!=maxh) ++end - store{inds, end, ind_maxh} - } - } - def get{found, j} = i32~~(m - (load{inds, j} &- found)) - tup{set, setm, usem, get} - } - {('⊒')} => { - def {inds} = vals; def {link} = links - store{link,0,0} - def set{j,je,{kr},dup} = { - # maxh entry either stores 0 or is the last entry and stores an - # index, so this preserves all values - ++je; store{inds, je, kr|load{inds,je}} - store{inds, j, 0} - setlabel{dup} - i1 := i+1 - store{link, i1, load{inds,j}} - store{inds, j, i1} - } - def get{found, j} = assert{0} - tup{set, {found,i}=>{}, {}=>{}, get} - } - } - - def lookup=makelabel{}; def abort=makelabel{}; def dup=makelabel{} - cc:usz = 0 # Collision counter - uniq:usz = 0 # Uniques inserted - i:usz=0; while (1) { - e := tern{m-i>b, i+b, m} - while (i < e) { - def ii = if (prim!='⊒') i else m-i-1 - h := hash_val{load{ip,ii}}; j := h>>sh - set_maxh{h==maxh, i} - kv := each{load{.,j}, tabs}; def {k,...kr} = kv - # Robin Hood insertion - j0 := j; je := j # Save value; end of chain (insert at j) - if (k != maxh) { - if (k == h) goto{dup} - do { - ++je; knv := each{load{.,je}, tabs}; def {kn,..._} = knv - def c = promote{T, h >= k} - j += c - if (kn == h) goto{dup} - each{store{.,je-c,.}, tabs, kv} - each{=, kv, knv} - } while (k != maxh) - cc += cast_i{usz, je-j0} - } - if (prim=='∊') uniq += promote{usz,h!=maxh} - store{hash, j, h} - set_tab{j, je, kr, 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{cc, 2} # Factor of 4 - if (cc >= stop) goto{abort} } + setlabel{insert_finish} } def hash_remove{j,h} = { @@ -489,9 +415,30 @@ fn hashtab{T, name}(rp:*rty{name}, iv:*void, m:usz, fv:*void, n:usz, links:ity{n each{{t} => store{t, jp, load{t,j}}, vals} } while (h!=maxh) } - - setlabel{lookup} - fixup{} + def memb_remove{uniq, has_maxh} = { + @for (ip over m) { + h := hash_val{ip}; j := h>>sh + def shortcut = makelabel{} + if (h == maxh) { + if (uniq==0) goto{shortcut} + has_maxh = 0 + } else { + k := load{hash,j} + if (k <= h) { + while (k < h) { ++j; k = load{hash,j} } + if (k == h) { + --uniq + if (uniq==0 and not has_maxh) { + setlabel{shortcut} + @for (rp over n) rp = 1 + goto{abort} + } + hash_remove{j,h} + } + } + } + } + } def prog_lookup{swap} = { # Progressive Index-of lookup def rev{a,b} = if (swap) tup{b,a} else tup{a,b} memset{*u32~~rp, ...rev{m,n}} @@ -514,9 +461,8 @@ fn hashtab{T, name}(rp:*rty{name}, iv:*void, m:usz, fv:*void, n:usz, links:ity{n } setlabel{shortcut} } - if (prim=='⊒') { - if (not swap) prog_lookup{0} else prog_lookup{1} - } else { + + def lookup_all{get_res} = { @for (rp, fp over n) { h := hash_val{fp}; j := h>>sh k := undefined{T}; while ((k=load{hash,j}) < h) ++j @@ -524,6 +470,65 @@ fn hashtab{T, name}(rp:*rty{name}, iv:*void, m:usz, fv:*void, n:usz, links:ity{n } } + match (prim, ...vals, ...links) { + {('∊')} => { + has_maxh:u1 = 0 + uniq:usz = 0 # Uniques inserted + def dup = makelabel{} + insert_all{ + {j,je,kr} => setlabel{dup}, + {found, i} => has_maxh |= found, + dup, uniq + } + if (swap) { + swap_sides{}; i=m # i==m return value is kind of dumb + memb_remove{uniq,has_maxh} # Remove values in ip from hash + } + try_vec_memb{T, hash, sz, sh, maxh, has_maxh, swap, rp, fp, n, abort} + end := maxh>>sh # Clip trailing maxh if it shouldn't be in the table + if (has_maxh) { while (load{hash,end}!=maxh) ++end } + else end += promote{T,ext} # Unreachable + lookup_all{{found, j} => promote{i8, swap ^ (found & (j { + has_maxh:u1 = 0 + ind_maxh:u32 = 0 + def dup = makelabel{} + def set{j,je,kr} = { + store{inds, j, m-i} # So it can be cleared with one &- in get{} + setlabel{dup} + } + def set_maxh{found, i} = { + ind_maxh |= i &- (found&~has_maxh) + has_maxh |= found + } + insert_all{set, set_maxh, dup} + if (has_maxh) { + end := maxh>>sh; while (load{hash,end}!=maxh) ++end + store{inds, end, ind_maxh} + } + lookup_all{{found, j} => i32~~(m - (load{inds, j} &- found))} + } + + {('⊒'), inds, link} => { + store{link,0,0} + def dup = makelabel{} + def set{j,je,{kr}} = { + # maxh entry either stores 0 or is the last entry and stores an + # index, so this preserves all values + ++je; store{inds, je, kr|load{inds,je}} + store{inds, j, 0} + setlabel{dup} + i1 := i+1 + store{link, i1, load{inds,j}} + store{inds, j, i1} + } + insert_all{set, {found,i}=>{}, dup} + if (not swap) prog_lookup{0} else prog_lookup{1} + } + } + setlabel{abort} hash_free{} i == m # Whether it finished