hashmaps, fast ⊐

This commit is contained in:
dzaima 2021-05-20 18:54:22 +03:00
parent 2ed797ce16
commit b8fe149d1f
13 changed files with 275 additions and 57 deletions

View File

@ -38,7 +38,7 @@ B m_c32slice(B p, u32* ptr) {
}
u32* c32arr_ptr(B x) { VT(x, t_c32arr); return c(C32Arr,x)->a; }
u32* c32arr_ptr(B x) { VTY(x, t_c32arr); return c(C32Arr,x)->a; }
u32* c32any_ptr(B x) { assert(isArr(x)); u8 t=v(x)->type; if(t==t_c32arr) return c(C32Arr,x)->a; assert(t==t_c32slice); return c(C32Slice,x)->a; }
B m_str8(usz sz, char* s) {
@ -84,8 +84,8 @@ bool eqStr(B w, u32* x) {
B c32arr_slice (B x, usz s) {return m_c32slice(x , c(C32Arr ,x)->a+s); }
B c32slice_slice(B x, usz s) { B r = m_c32slice(inc(c(Slice,x)->p), c(C32Slice,x)->a+s); dec(x); return r; }
B c32arr_get (B x, usz n) { VT(x,t_c32arr ); return m_c32(c(C32Arr ,x)->a[n]); }
B c32slice_get(B x, usz n) { VT(x,t_c32slice); return m_c32(c(C32Slice,x)->a[n]); }
B c32arr_get (B x, usz n) { VTY(x,t_c32arr ); return m_c32(c(C32Arr ,x)->a[n]); }
B c32slice_get(B x, usz n) { VTY(x,t_c32slice); return m_c32(c(C32Slice,x)->a[n]); }
void c32arr_free(Value* x) { decSh(x); }
bool c32arr_canStore(B x) { return isC32(x); }

View File

@ -38,7 +38,7 @@ B m_f64slice(B p, f64* ptr) {
}
f64* f64arr_ptr(B x) { VT(x, t_f64arr); return c(F64Arr,x)->a; }
f64* f64arr_ptr(B x) { VTY(x, t_f64arr); return c(F64Arr,x)->a; }
f64* f64any_ptr(B x) { assert(isArr(x)); u8 t=v(x)->type; if(t==t_f64arr) return c(F64Arr,x)->a; assert(t==t_f64slice); return c(F64Slice,x)->a; }
NOINLINE B m_caf64(usz sz, f64* a) {
@ -61,8 +61,8 @@ F64Arr* toF64Arr(B x) {
B f64arr_slice (B x, usz s) {return m_f64slice(x , c(F64Arr ,x)->a+s); }
B f64slice_slice(B x, usz s) { B r = m_f64slice(inc(c(Slice,x)->p), c(F64Slice,x)->a+s); dec(x); return r; }
B f64arr_get (B x, usz n) { VT(x,t_f64arr ); return m_f64(c(F64Arr ,x)->a[n]); }
B f64slice_get(B x, usz n) { VT(x,t_f64slice); return m_f64(c(F64Slice,x)->a[n]); }
B f64arr_get (B x, usz n) { VTY(x,t_f64arr ); return m_f64(c(F64Arr ,x)->a[n]); }
B f64slice_get(B x, usz n) { VTY(x,t_f64slice); return m_f64(c(F64Slice,x)->a[n]); }
void f64arr_free(Value* x) { decSh(x); }
bool f64arr_canStore(B x) { return q_f64(x); }

View File

@ -73,15 +73,15 @@ B m_fillslice(B p, B* ptr) {
return tag(r, ARR_TAG);
}
B* fillarr_ptr(B x) { VT(x,t_fillarr); return c(FillArr,x)->a; }
B* fillarr_ptr(B x) { VTY(x,t_fillarr); return c(FillArr,x)->a; }
B fillarr_slice (B x, usz s) {return m_fillslice(x , c(FillArr ,x)->a+s); }
B fillslice_slice(B x, usz s) { B r = m_fillslice(inc(c(Slice,x)->p), c(FillSlice,x)->a+s); dec(x); return r; }
B fillarr_get (B x, usz n) { VT(x,t_fillarr ); return inc(c(FillArr ,x)->a[n]); }
B fillslice_get (B x, usz n) { VT(x,t_fillslice); return inc(c(FillSlice,x)->a[n]); }
B fillarr_getU (B x, usz n) { VT(x,t_fillarr ); return c(FillArr ,x)->a[n] ; }
B fillslice_getU(B x, usz n) { VT(x,t_fillslice); return c(FillSlice,x)->a[n] ; }
B fillarr_get (B x, usz n) { VTY(x,t_fillarr ); return inc(c(FillArr ,x)->a[n]); }
B fillslice_get (B x, usz n) { VTY(x,t_fillslice); return inc(c(FillSlice,x)->a[n]); }
B fillarr_getU (B x, usz n) { VTY(x,t_fillarr ); return c(FillArr ,x)->a[n] ; }
B fillslice_getU(B x, usz n) { VTY(x,t_fillslice); return c(FillSlice,x)->a[n] ; }
void fillarr_free(Value* x) {
decSh(x);
B* p = ((FillArr*)x)->a;

View File

@ -174,29 +174,64 @@ B indexOf_c1(B t, B x) {
if (isAtm(x)) thrM("⊐: 𝕩 cannot have rank 0");
usz xia = a(x)->ia;
if (xia==0) { dec(x); return inc(bi_emptyIVec); }
if (rnk(x)==1 && TI(x).elType==el_i32) {
i32* xp = i32any_ptr(x);
i32 min=I32_MAX, max=I32_MIN;
// if (rnk(x)==1 && TI(x).elType==el_i32) {
// i32* xp = i32any_ptr(x);
// i32 min=I32_MAX, max=I32_MIN;
// for (usz i = 0; i < xia; i++) {
// i32 c = xp[i];
// if (c<min) min = c;
// if (c>max) max = c;
// }
// i32 dst = 1 + max-(i64)min;
// if ((dst<xia*5 || dst<50) && min!=I32_MIN) {
// i32* rp; B r = m_i32arrv(&rp, xia);
// i32 tmp[dst];
// for (usz i = 0; i < dst; i++) tmp[i] = I32_MIN;
// i32* tc = tmp-min;
// i32 ctr = 0;
// for (usz i = 0; i < xia; i++) {
// i32 c = xp[i];
// if (tc[c]==I32_MIN) tc[c] = ctr++;
// rp[i] = tc[c];
// }
// dec(x);
// return r;
// }
// }
// if (rnk(x)==1) { // relies on equal hashes implying equal objects, which has like a 2⋆¯64 chance of being false per item
// // u64 s = nsTime();
// i32* rp; B r = m_i32arrv(&rp, xia);
// u64 size = xia*2;
// wyhashmap_t idx[size];
// i32 val[size];
// for (i64 i = 0; i < size; i++) { idx[i] = 0; val[i] = -1; }
// BS2B xget = TI(x).get;
// i32 ctr = 0;
// for (usz i = 0; i < xia; i++) {
// u64 hash = bqn_hash(xget(x,i), wy_secret);
// u64 p = wyhashmap(idx, size, &hash, 8, true, wy_secret);
// if (val[p]==-1) val[p] = ctr++;
// rp[i] = val[p];
// }
// dec(x);
// // u64 e = nsTime(); q1+= e-s;
// return r;
// }
if (rnk(x)==1) {
// u64 s = nsTime();
i32* rp; B r = m_i32arrv(&rp, xia);
H_b2i* map = m_b2i(64);
BS2B xgetU = TI(x).getU;
i32 ctr = 0;
for (usz i = 0; i < xia; i++) {
i32 c = xp[i];
if (c<min) min = c;
if (c>max) max = c;
}
i32 dst = 1 + max-(i64)min;
if ((dst<xia*5 || dst<50) && min!=I32_MIN) {
i32* rp; B r = m_i32arrv(&rp, xia);
i32 tmp[dst];
for (usz i = 0; i < dst; i++) tmp[i] = I32_MIN;
i32* tc = tmp-min;
i32 ctr = 0;
for (usz i = 0; i < xia; i++) {
i32 c = xp[i];
if (tc[c]==I32_MIN) tc[c] = ctr++;
rp[i] = tc[c];
}
dec(x);
return r;
bool had; u64 p = mk_b2i(&map, xgetU(x,i), &had);
if (had) rp[i] = map->ent[p].val;
else rp[i] = map->ent[p].val = ctr++;
}
free_b2i(map);
dec(x);
// u64 e = nsTime(); q1+= e-s;
return r;
}
return c1(rt_indexOf, x);
}

View File

@ -39,6 +39,7 @@
#define NOINLINE __attribute__ ((noinline))
#define NORETURN __attribute__ ((noreturn))
#define AUTO __auto_type
#define LIKELY(X) __builtin_expect(X,1)
#define RARE(X) __builtin_expect(X,0)
typedef u32 usz;
@ -86,10 +87,10 @@ enum Type {
/*19*/ t_hslice, t_i8slice, t_i32slice, t_fillslice, t_c32slice, t_f64slice,
/*25*/ t_comp, t_block, t_body, t_scope,
/*29*/ t_ns, t_nsDesc, t_fldAlias,
/*31*/ t_freed, t_harrPartial,
/*29*/ t_ns, t_nsDesc, t_fldAlias, t_hashmap,
/*33*/ t_freed, t_harrPartial,
#ifdef RT_PERF
/*33*/ t_funPerf, t_md1Perf, t_md2Perf,
/*35*/ t_funPerf, t_md1Perf, t_md2Perf,
#endif
t_COUNT
};
@ -289,7 +290,7 @@ u64 envPrevHeight; // envStart+prevEnvHeight will give the original envCurr
#define sprnk(X,R) (X->extra=(R))
#define rnk(X ) prnk(v(X))
#define srnk(X,R) sprnk(v(X),R)
#define VT(X,T) assert(isVal(X) && v(X)->type==(T))
#define VTY(X,T) assert(isVal(X) && v(X)->type==(T))
void print_vmStack();
#ifdef DEBUG

View File

@ -23,27 +23,27 @@ HArr_p m_harrs(usz ia, usz* ctr) { // writes just ia
gsAdd(r);
return harr_parts(r);
}
B harr_fv(HArr_p p) { VT(p.b, t_harrPartial);
B harr_fv(HArr_p p) { VTY(p.b, t_harrPartial);
p.c->type = t_harr;
p.c->sh = &p.c->ia;
srnk(p.b, 1);
gsPop();
return p.b;
}
B harr_fc(HArr_p p, B x) { VT(p.b, t_harrPartial);
B harr_fc(HArr_p p, B x) { VTY(p.b, t_harrPartial);
p.c->type = t_harr;
arr_shCopy(p.b, x);
gsPop();
return p.b;
}
B harr_fcd(HArr_p p, B x) { VT(p.b, t_harrPartial);
B harr_fcd(HArr_p p, B x) { VTY(p.b, t_harrPartial);
p.c->type = t_harr;
arr_shCopy(p.b, x);
dec(x);
gsPop();
return p.b;
}
usz* harr_fa(HArr_p p, ur r) { VT(p.b, t_harrPartial);
usz* harr_fa(HArr_p p, ur r) { VTY(p.b, t_harrPartial);
p.c->type = t_harr;
gsPop();
return arr_shAllocR(p.b, r);
@ -133,7 +133,7 @@ B toKCells(B x, ur k) {
}
B* harr_ptr(B x) { VT(x,t_harr); return c(HArr,x)->a; }
B* harr_ptr(B x) { VTY(x,t_harr); return c(HArr,x)->a; }
HArr* toHArr(B x) {
if (v(x)->type==t_harr) return c(HArr,x);
@ -174,10 +174,10 @@ B harr_slice (B x, usz s) {return m_hslice(x , c(HArr ,x)->a+s
B hslice_slice(B x, usz s) { B r = m_hslice(inc(c(Slice,x)->p), c(HSlice,x)->a+s); dec(x); return r; }
B harr_get (B x, usz n) { VT(x,t_harr ); return inc(c(HArr ,x)->a[n]); }
B hslice_get (B x, usz n) { VT(x,t_hslice); return inc(c(HSlice,x)->a[n]); }
B harr_getU (B x, usz n) { VT(x,t_harr ); return c(HArr ,x)->a[n] ; }
B hslice_getU(B x, usz n) { VT(x,t_hslice); return c(HSlice,x)->a[n] ; }
B harr_get (B x, usz n) { VTY(x,t_harr ); return inc(c(HArr ,x)->a[n]); }
B hslice_get (B x, usz n) { VTY(x,t_hslice); return inc(c(HSlice,x)->a[n]); }
B harr_getU (B x, usz n) { VTY(x,t_harr ); return c(HArr ,x)->a[n] ; }
B hslice_getU(B x, usz n) { VTY(x,t_hslice); return c(HSlice,x)->a[n] ; }
void harr_free(Value* x) {
decSh(x);
B* p = ((HArr*)x)->a; // don't use harr_ptr so type isn't checked

View File

@ -34,18 +34,18 @@ B bqn_squeeze(B x) {
} else return x;
}
u64 bqn_hash(B x, const u64 secret[4]) { // consumes (because otherwise squeezing is hard as in-place cannot be done because slices)
u64 bqn_hash(B x, const u64 secret[4]) { // doesn't consume
if (isAtm(x)) {
if (q_f64(x)) return wyhash64(secret[0], x.u);
if (isC32(x)) return wyhash64(secret[1], x.u);
assert(isVal(x));
dec(x);
return wyhash64(secret[2], x.u);
}
inc(x);
x = bqn_squeeze(x);
u64 shHash = wyhash(a(x)->sh, rnk(x)*sizeof(usz), 0, secret);
u8 xe = TI(x).elType;
usz xia = a(x)->ia;
u64 shHash = wyhash(a(x)->sh, rnk(x)*sizeof(usz), 0, secret);
u64 r;
if (xe==el_i32) {
r = wyhash(i32any_ptr(x), xia*4, shHash, secret);
@ -56,10 +56,42 @@ u64 bqn_hash(B x, const u64 secret[4]) { // consumes (because otherwise squeezin
} else {
assert(xe==el_B);
u64 data[xia];
BS2B xget = TI(x).get;
for (usz i = 0; i < xia; i++) data[i] = bqn_hash(xget(x, i), secret);
BS2B xgetU = TI(x).getU;
for (usz i = 0; i < xia; i++) data[i] = bqn_hash(xgetU(x, i), secret);
r = wyhash(data, xia*8, shHash, secret);
}
dec(x);
return r;
}
u64 wy_secret[4];
u64 bqn_hashP(B x, const u64 secret[4]) { // bqn_hash but never zero
u64 r = bqn_hash(x, secret);
return LIKELY(r)?r:secret[3]; // bias towards secret[3], whatever
}
#define N(X) X##_b2i
#define HT u64
#define KT B
#define H1(K) bqn_hashP(K, wy_secret)
#define H2(K,h1) h1
#define H1R(K,h2) h2
#define EMPTY(S,K) ((S)==0)
#define HDEF 0
#define KEYS
#define EQUAL(A,B) equal(A,B)
#define VALS
#define VT i32
#include "hashmap.c"
void hash_init() {
u64 bad1=0xa0761d6478bd642full; // values wyhash64 is afraid of
u64 bad2=0xe7037ed1a0b428dbull;
again:
make_secret(nsTime(), wy_secret);
for (u64 i = 0; i < 4; i++) if(wy_secret[i]==bad1 || wy_secret[i]==bad2) goto again;
}

147
src/hashmap.c Normal file
View File

@ -0,0 +1,147 @@
// args:
// N(X) - X##_mapName
// HT - stored hash type (u32 or u64)
// KT - key type
// H1(K) - function to compute index in hashmap, returns u64; the expensive part
// H2(K,h1) - function to compute HT (returning just h1 (or K if applicable) is safe)
// H1R(K,h2) - calculate H1 from H2, preferrably fast
// EMPTY(S,K) - test whether value counts as unpopulated (ignore K if !KEYS)
// HDEF - if defined, the value to initialize HT to (otherwise it's undefined)
// KEYS - whether to separately store and check keys (otherwise only HT is compared)
// EQUAL(A,B) - compare two keys
// KDEF - if defined, the value to initialize keys to (otherwise it's undefined)
// VALS - whether to store values corresponding to keys (otherwise this is a hashset)
// VT - value type
#define Map N(H)
#define Ent N(Ent)
#ifdef KEYS
#define IFKEY(...) __VA_ARGS__
#else
#define IFKEY(...)
#endif
#ifdef VALS
#define IFVAL(...) __VA_ARGS__
#else
#define IFVAL(...)
#endif
typedef struct Ent {
#ifdef KEYS
KT key;
#endif
HT hash;
#ifdef VALS
VT val;
#endif
} Ent;
typedef struct Map {
struct Value;
u64 pop; // count of defined entries
u64 mask; // sz-1
u64 sz; // count of allocated entries, a power of 2
Ent ent[];
} Map;
Map* N(m) (u64 sz) {
assert(sz && (sz & sz-1)==0);
Map* r = mm_allocN(fsizeof(Map,ent,Ent,sz), t_hashmap);
#ifdef HDEF
for (u64 i = 0; i < sz; i++) r->ent[i].hash = HDEF;
#endif
#ifdef KDEF
for (u64 i = 0; i < sz; i++) r->ent[i].key = KDEF;
#endif
r->sz = sz;
r->mask = sz-1;
r->pop = 0;
return r;
}
void N(free) (Map* m) {
mm_free((Value*) m);
}
static inline u64 N(find) (Map* m, KT k, u64 h1, u64 h2, bool* had) {
assert(m->sz > m->pop);
*had = true;
u64 mask = m->mask;
u64 p = h1 & mask;
while (true) {
Ent e = m->ent[p];
if (e.hash==h2 IFKEY(&& LIKELY(EQUAL(e.key, k)))) return p;
if (EMPTY(e.hash, e.key)) { *had=false; return p; }
if (RARE(p++==mask)) p = 0;
}
}
static inline void N(qins) (Map* m, u64 h1, HT h2, KT k IFVAL(, VT v)) { // if guaranteed that k doesn't exist in the map yet and there's space for this
u64 mask = m->mask;
u64 p = h1 & mask;
while (true) {
u64 ch2 = m->ent[p].hash;
if (EMPTY(ch2, m->ent[p].key)) {
m->ent[p].hash = h2;
IFKEY(m->ent[p].key = k);
IFVAL(m->ent[p].val = v);
m->pop++;
return;
}
if (p++==mask) p = 0;
}
}
void N(dbl) (Map** m) {
Map* pm = *m;
u64 psz = pm->sz;
Map* nm = N(m)(psz*2);
for (u64 i = 0; i < psz; i++) {
Ent e = pm->ent[i];
if(!EMPTY(e.hash, e.key)) N(qins)(nm, H1R(e.key, e.hash), e.hash, e.key IFVAL(, e.val));
}
mm_free((Value*)pm);
*m = nm;
}
static inline u64 N(mk) (Map** mp, KT k, bool* had) {
Map* m = *mp;
if (m->pop*2 > m->sz) { N(dbl)(mp); m=*mp; }
u64 h1 = H1(k); u64 h2 = H2(k, h1);
u64 p = N(find)(m, k, h1, h2, had);
if (*had) return p;
m->ent[p].hash = h2;
IFKEY(m->ent[p].key = k);
m->pop++;
return p;
}
#ifdef VALS
static inline bool N(ins) (Map** mp, KT k, VT v) { // returns whether element was replaced
bool had; u64 p = N(mk)(mp, k, &had);
(*mp)->ent[p].val = v;
return had;
}
#endif
#undef IFKEY
#undef IFVAL
#undef Ent
#undef Map
#undef N
#undef HT
#undef KT
#undef H1
#undef H2
#undef EMPTY
#undef HDEF
#ifdef KEYS
#undef KEYS
#undef EQUAL
#undef KDEF
#endif
#ifdef VALS
#undef VALS
#undef VT
#endif

View File

@ -38,7 +38,7 @@ B m_i32slice(B p, i32* ptr) {
}
i32* i32arr_ptr(B x) { VT(x, t_i32arr); return c(I32Arr,x)->a; }
i32* i32arr_ptr(B x) { VTY(x, t_i32arr); return c(I32Arr,x)->a; }
i32* i32any_ptr(B x) { assert(isArr(x)); u8 t=v(x)->type; if(t==t_i32arr) return c(I32Arr,x)->a; assert(t==t_i32slice); return c(I32Slice,x)->a; }
NOINLINE B m_cai32(usz ia, i32* a) {
@ -67,8 +67,8 @@ I32Arr* toI32Arr(B x) { // assumes it's possible
B i32arr_slice (B x, usz s) {return m_i32slice(x , c(I32Arr ,x)->a+s); }
B i32slice_slice(B x, usz s) { B r = m_i32slice(inc(c(Slice,x)->p), c(I32Slice,x)->a+s); dec(x); return r; }
B i32arr_get (B x, usz n) { VT(x,t_i32arr ); return m_i32(c(I32Arr ,x)->a[n]); }
B i32slice_get(B x, usz n) { VT(x,t_i32slice); return m_i32(c(I32Slice,x)->a[n]); }
B i32arr_get (B x, usz n) { VTY(x,t_i32arr ); return m_i32(c(I32Arr ,x)->a[n]); }
B i32slice_get(B x, usz n) { VTY(x,t_i32slice); return m_i32(c(I32Slice,x)->a[n]); }
void i32arr_free(Value* x) { decSh(x); }
bool i32arr_canStore(B x) { return q_i32(x); }

View File

@ -76,6 +76,7 @@ int main(int argc, char* argv[]) {
gc_add(srcB);
for (i32 i = 0; i < 100; i++) { dec(bqn_exec(inc(srcB), bi_N, bi_N)); gc_maybeGC(); }
rtPerf_print();
CTR_FOR(CTR_PRINT)
exit(0);
#endif
bqn_setComp(bqn_exec(srcB, bi_N, bi_N));

View File

@ -518,7 +518,7 @@ void printAllocStats() {
_Thread_local B comp_currPath;
_Thread_local B comp_currArgs;
#define FOR_INIT(F) F(hdr) F(harr) F(fillarr) F(i32arr) F(c32arr) F(f64arr) F(fns) F(sfns) F(arith) F(grade) F(md1) F(md2) F(sysfn) F(derv) F(comp) F(rtPerf) F(ns) F(load)
#define FOR_INIT(F) F(hdr) F(harr) F(fillarr) F(i32arr) F(c32arr) F(f64arr) F(hash) F(fns) F(sfns) F(arith) F(grade) F(md1) F(md2) F(sysfn) F(derv) F(comp) F(rtPerf) F(ns) F(load)
#define F(X) static inline void X##_init();
FOR_INIT(F)
#undef F

View File

@ -164,13 +164,15 @@ B cmp_c2(B t, B w, B x) {
B hash_c2(B t, B w, B x) {
u64 secret[4]; make_secret(o2i64(w), secret);
u64 rv = bqn_hash(x, secret);
dec(x);
i32* rp; B r = m_i32arrv(&rp, 4);
rp[0] = (u16)(rv>>48); rp[1] = (u16)(rv>>32);
rp[2] = (u16)(rv>>16); rp[3] = (u16)(rv );
return r;
}
B hash_c1(B t, B x) {
u64 rv = bqn_hash(x, _wyp);
u64 rv = bqn_hash(x, wy_secret);
dec(x);
i32* rp; B r = m_i32arrv(&rp, 4);
rp[0] = (u16)(rv>>48); rp[1] = (u16)(rv>>32);
rp[2] = (u16)(rv>>16); rp[3] = (u16)(rv );

View File

@ -211,7 +211,7 @@ void v_set(Scope* pscs[], B s, B x, bool upd) { // doesn't consume
}
sc->vars[(u32)s.u] = inc(x);
} else {
VT(s, t_harr);
VTY(s, t_harr);
B* sp = harr_ptr(s);
usz ia = a(s)->ia;
if (isAtm(x) || !eqShape(s, x)) {
@ -244,7 +244,7 @@ B v_get(Scope* pscs[], B s) { // get value representing s, replacing with bi_opt
sc->vars[(u32)s.u] = bi_optOut;
return r;
} else {
VT(s, t_harr);
VTY(s, t_harr);
usz ia = a(s)->ia;
B* sp = harr_ptr(s);
HArr_p r = m_harrUv(ia);