From d8e4e7d96fbc29a2dd595129ece2fb2f748b2263 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sat, 29 Oct 2022 22:02:57 -0400 Subject: [PATCH 01/11] =?UTF-8?q?Unify=20small-type=20and=20small-range=20?= =?UTF-8?q?tables=20for=20Classify=20(=E2=8A=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 2a6fade8..65ab5dc3 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -4,6 +4,7 @@ B not_c1(B t, B x); B shape_c1(B t, B x); +B slash_c2(B t, B w, B x); #define GRADE_UD(U,D) U #include "radix.h" @@ -192,7 +193,6 @@ B count_c1(B t, B x) { return r; } -extern B rt_indexOf; B indexOf_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("⊐: 𝕩 cannot have rank 0"); usz n = *SH(x); @@ -215,6 +215,13 @@ B indexOf_c1(B t, B x) { rp[i]=s; u+=u==s; \ } \ decG(x); TFREE(uniq); return r; + #define DOTAB(T) \ + i32 u=0; \ + for (usz i=0; i8 && nmax) max = c; } i64 dst = 1 + (max-(i64)min); - if ((dst1) x = toCells(x); i32* rp; B r = m_i32arrv(&rp, n); @@ -274,8 +272,6 @@ B indexOf_c1(B t, B x) { return r; } -B slash_c2(B t, B w, B x); -extern B rt_find; B find_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("⍷: Argument cannot have rank 0"); usz n = *SH(x); From 8a551ab5ce99c9b8d2bceea20bdbb85e11d0c17a Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 30 Oct 2022 21:00:35 -0400 Subject: [PATCH 02/11] =?UTF-8?q?Add=2032-bit=20hash=20table=20for=20Class?= =?UTF-8?q?ify=20(=E2=8A=92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 65ab5dc3..9cb9fca6 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -6,6 +6,18 @@ B not_c1(B t, B x); B shape_c1(B t, B x); B slash_c2(B t, B w, B x); +#if defined(__SSE4_2__) +static inline u32 hash32(u32 x) { return _mm_crc32_u32(0x973afb51, x); } +#else +// Murmur3 +static inline u32 hash32(u32 x) { + x ^= x >> 16; x *= 0x85ebca6b; + x ^= x >> 13; x *= 0xc2b2ae35; + x ^= x >> 16; + return x; +} +#endif + #define GRADE_UD(U,D) U #include "radix.h" u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) { @@ -237,7 +249,9 @@ B indexOf_c1(B t, B x) { #undef LOOKUP if (lw==5) { - if (n<32) { BRUTE(32); } + if (n<12) { BRUTE(32); } + B r; + i32* rp; r = m_i32arrv(&rp, n); i32* xp = tyany_ptr(x); i32 min=I32_MAX, max=I32_MIN; for (usz i = 0; i < n; i++) { @@ -247,13 +261,29 @@ B indexOf_c1(B t, B x) { } i64 dst = 1 + (max-(i64)min); if (dstsz) for (usz j=0; j Date: Sun, 30 Oct 2022 21:12:12 -0400 Subject: [PATCH 03/11] =?UTF-8?q?Resizable=20hash=20table=20for=20Mark=20F?= =?UTF-8?q?irsts=20(=E2=88=8A)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 57 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 9cb9fca6..d13a1520 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -104,11 +104,62 @@ B memberOf_c1(B t, B x) { if (lw == 4) { if (n<8) { BRUTE(16); } else { LOOKUP(16); } } #undef LOOKUP if (lw == 5) { - if (n<=32) { BRUTE(32); } + if (n<=12) { BRUTE(32); } + + // Resizable hash table, fall back to radix lookup + u32* xp = (u32*)xv; + i8* rp; B r = m_i8arrv(&rp, n); + usz log = 64 - CLZ(n); + usz msl = (64 - CLZ(n+n/2)) + 1; if (msl>18) msl=18; + usz sh = 32 - (msl<14? msl : 14); // Shift to fit to table + usz sz = 1 << (32 - sh); // Initial size + usz msz = 1 << msl; // Max sz + usz b = 64; // Block size + // Resize or abort if more than 1/2^thresh collisions/element + #define THRESH sz==msz? 1 : sz>=(1<<13)? 2 : 3 + usz thresh = THRESH; + // Filling e slots past the end requires e*(e+1)/2 collisions, so + // n entries with 1/2 each can fill b? i+b : n; + for (; i < e; i++) { + u32 h = hash32(xp[i]); u32 j0 = h>>sh, j = j0; u32 k; + while (k=hash[j], k!=h & k!=x0) j++; + cc += j-j0; + hash[j] = h; + rp[i] = k != h; + } + if (i == n) break; + if (cc >= e>>thresh) { + if (sz == msz) break; // Abort + // Resize hash + usz m = 1 + (sz < msz/4); + sh -= m; + usz dif = sz; sz <<= m; dif = sz-dif; + hash -= dif; + usz j = 0; + for (; j < dif; j++) hash[j] = x0; + for (; j < sz + ext; j++) { + u32 h = hash[j]; if (h==x0) continue; hash[j] = x0; + u32 k = h>>sh; while (hash[k]!=x0) k++; + hash[k] = h; + } + thresh = THRESH; + } + } + #undef THRESH + TFREE(hash0); + if (i==n) { decG(x); return taga(cpyBitArr(r)); } + // Radix-assisted lookup usz rx = 256, tn = 1<<16; // Radix; table length u32* v0 = (u32*)xv; - i8* r0; B r = m_i8arrv(&r0, n); + i8* r0 = rp; TALLOC(u8, alloc, 6*n+(4+(tn>3*n?tn:3*n)+(2*rx+1)*sizeof(usz))); // timeline @@ -124,7 +175,7 @@ B memberOf_c1(B t, B x) { u8 *tab= (u8 *)(r1); // tn [+] tab tn ##### RADIX_LOOKUP_32(1, =0) - return num_squeeze(r); + return taga(cpyBitArr(r)); } #undef BRUTE From dc7389a660e0e86ed0f7a87828a2d7ab6885f63f Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sat, 5 Nov 2022 18:29:53 -0400 Subject: [PATCH 04/11] Mark Firsts hash table resizing policy tweaks --- src/builtins/selfsearch.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index d13a1520..6e2e0297 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -110,17 +110,17 @@ B memberOf_c1(B t, B x) { u32* xp = (u32*)xv; i8* rp; B r = m_i8arrv(&rp, n); usz log = 64 - CLZ(n); - usz msl = (64 - CLZ(n+n/2)) + 1; if (msl>18) msl=18; - usz sh = 32 - (msl<14? msl : 14); // Shift to fit to table + usz msl = (64 - CLZ(n+n/2)) + 1; if (msl>20) msl=20; + usz sh = 32 - (msl<14? msl : 12+(msl&1)); // Shift to fit to table usz sz = 1 << (32 - sh); // Initial size usz msz = 1 << msl; // Max sz usz b = 64; // Block size // Resize or abort if more than 1/2^thresh collisions/element - #define THRESH sz==msz? 1 : sz>=(1<<13)? 2 : 3 + #define THRESH sz==msz? 1 : sz>=(1<<15)? 3 : 5 usz thresh = THRESH; // Filling e slots past the end requires e*(e+1)/2 collisions, so - // n entries with 1/2 each can fill = e>>thresh) { - if (sz == msz) break; // Abort - // Resize hash - usz m = 1 + (sz < msz/4); - sh -= m; - usz dif = sz; sz <<= m; dif = sz-dif; + i64 dc = (i64)cc - (i>>thresh); + if (dc >= 0) { + if (sz == msz || (i < n/2 && sz >= 1<<18)) break; // Abort + // Avoid resizing if close to the end + if (cc>(5+log-(32-sh))) continue; + // Resize hash, factor of 4 + usz m = 2; + usz dif = sz*((1<>sh; while (hash[k]!=x0) k++; + u32 k0 = h>>sh, k = k0; while (hash[k]!=x0) k++; + cc += k-k0; hash[k] = h; } + if (cc >= n/2) break; thresh = THRESH; } } From 37d7694c52f5e37b8f363a836c0a55355b42dd00 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sat, 5 Nov 2022 20:49:39 -0400 Subject: [PATCH 05/11] =?UTF-8?q?64-bit=20Mark=20Firsts=20(=E2=88=8A)=20ha?= =?UTF-8?q?sh=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 133 +++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 53 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 6e2e0297..9e2fae4d 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -17,6 +17,12 @@ static inline u32 hash32(u32 x) { return x; } #endif +static inline u64 hash64(u64 x) { + x ^= x >> 33; x *= 0xff51afd7ed558ccd; + x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53; + x ^= x >> 33; + return x; +} #define GRADE_UD(U,D) U #include "radix.h" @@ -67,6 +73,20 @@ u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) { } \ decG(x); TFREE(alloc); +static bool canCompare64_norm(B x, usz n) { + u8 e = TI(x,elType); + if (e == el_B) return 0; + if (e == el_f64) { + f64* p = f64any_ptr(x); + for (usz i = 0; i < n; i++) { + f64 v = p[i]; + if (v!=v) return 0; + if (v==0) p[i]=0; + } + } + return 1; +} + B memberOf_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("∊: Argument cannot have rank 0"); usz n = *SH(x); @@ -103,64 +123,64 @@ B memberOf_c1(B t, B x) { if (lw == 3) { if (n<8) { BRUTE(8); } else { LOOKUP(8); } } if (lw == 4) { if (n<8) { BRUTE(16); } else { LOOKUP(16); } } #undef LOOKUP + #define HASHTAB(T, W, RAD, STOP, THRESH) \ + T* xp = (T*)xv; \ + usz log = 64 - CLZ(n); \ + usz msl = (64 - CLZ(n+n/2)) + 1; if (RAD && msl>20) msl=20; \ + usz sh = W - (msl<14? msl : 12+(msl&1)); /* Shift to fit to table */ \ + usz sz = 1 << (W - sh); /* Initial size */ \ + usz msz = 1 << msl; /* Max sz */ \ + usz b = 64; /* Block size */ \ + /* Resize or abort if more than 1/2^thresh collisions/element */ \ + usz thresh = THRESH; \ + /* Filling e slots past the end requires e*(e+1)/2 collisions, so */ \ + /* n entries with <2 each can fill b? i+b : n; \ + for (; i < e; i++) { \ + T h = hash##W(xp[i]); T j0 = h>>sh, j = j0; T k; \ + while (k=hash[j], k!=h & k!=x0) j++; \ + cc += j-j0; \ + hash[j] = h; \ + rp[i] = k != h; \ + } \ + if (i == n) break; \ + i64 dc = (i64)cc - (i>>thresh); \ + if (dc >= 0) { \ + if (sz == msz || (RAD && i < n/2 && sz >= 1<<18)) break; /*Abort*/ \ + /* Avoid resizing if close to the end */ \ + if (cc>(5+log-(W-sh))) continue;\ + /* Resize hash, factor of 4 */ \ + usz m = 2; \ + usz dif = sz*((1<>sh, k = k0; while (hash[k]!=x0) k++; \ + cc += k-k0; \ + hash[k] = h; \ + } \ + if (cc >= STOP) break; \ + thresh = THRESH; \ + } \ + } \ + TFREE(hash0); \ + if (i==n) { decG(x); return taga(cpyBitArr(r)); } if (lw == 5) { if (n<=12) { BRUTE(32); } // Resizable hash table, fall back to radix lookup - u32* xp = (u32*)xv; i8* rp; B r = m_i8arrv(&rp, n); - usz log = 64 - CLZ(n); - usz msl = (64 - CLZ(n+n/2)) + 1; if (msl>20) msl=20; - usz sh = 32 - (msl<14? msl : 12+(msl&1)); // Shift to fit to table - usz sz = 1 << (32 - sh); // Initial size - usz msz = 1 << msl; // Max sz - usz b = 64; // Block size - // Resize or abort if more than 1/2^thresh collisions/element - #define THRESH sz==msz? 1 : sz>=(1<<15)? 3 : 5 - usz thresh = THRESH; - // Filling e slots past the end requires e*(e+1)/2 collisions, so - // n entries with 1/2 each can fill b? i+b : n; - for (; i < e; i++) { - u32 h = hash32(xp[i]); u32 j0 = h>>sh, j = j0; u32 k; - while (k=hash[j], k!=h & k!=x0) j++; - cc += j-j0; - hash[j] = h; - rp[i] = k != h; - } - if (i == n) break; - i64 dc = (i64)cc - (i>>thresh); - if (dc >= 0) { - if (sz == msz || (i < n/2 && sz >= 1<<18)) break; // Abort - // Avoid resizing if close to the end - if (cc>(5+log-(32-sh))) continue; - // Resize hash, factor of 4 - usz m = 2; - usz dif = sz*((1<>sh, k = k0; while (hash[k]!=x0) k++; - cc += k-k0; - hash[k] = h; - } - if (cc >= n/2) break; - thresh = THRESH; - } - } - #undef THRESH - TFREE(hash0); - if (i==n) { decG(x); return taga(cpyBitArr(r)); } + HASHTAB(u32, 32, 1, n/2, sz==msz? 1 : sz>=(1<<15)? 3 : 5) // Radix-assisted lookup usz rx = 256, tn = 1<<16; // Radix; table length @@ -183,6 +203,13 @@ B memberOf_c1(B t, B x) { RADIX_LOOKUP_32(1, =0) return taga(cpyBitArr(r)); } + if (lw == 6 && canCompare64_norm(x, n)) { + if (n<=12) { BRUTE(64); } + i8* rp; B r = m_i8arrv(&rp, n); + HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5) + decG(r); // Fall through + } + #undef HASHTAB #undef BRUTE if (RNK(x)>1) x = toCells(x); From 9adfb13355299cbd3a99f3b19f029ffca73e972f Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 6 Nov 2022 09:38:51 -0500 Subject: [PATCH 06/11] Extend Mark Firsts hash table to handle Classify --- src/builtins/selfsearch.c | 186 ++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 9e2fae4d..2ef284d6 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -24,6 +24,20 @@ static inline u64 hash64(u64 x) { return x; } +static bool canCompare64_norm(B x, usz n) { + u8 e = TI(x,elType); + if (e == el_B) return 0; + if (e == el_f64) { + f64* p = f64any_ptr(x); + for (usz i = 0; i < n; i++) { + f64 v = p[i]; + if (v!=v) return 0; + if (v==0) p[i]=0; + } + } + return 1; +} + #define GRADE_UD(U,D) U #include "radix.h" u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) { @@ -73,19 +87,60 @@ u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) { } \ decG(x); TFREE(alloc); -static bool canCompare64_norm(B x, usz n) { - u8 e = TI(x,elType); - if (e == el_B) return 0; - if (e == el_f64) { - f64* p = f64any_ptr(x); - for (usz i = 0; i < n; i++) { - f64 v = p[i]; - if (v!=v) return 0; - if (v==0) p[i]=0; - } - } - return 1; -} +// Resizing hash table, with fallback +#define SELFHASHTAB(T, W, RAD, STOP, RES0, RESULT, RESWRITE, THRESHMUL, THRESH, AUXSIZE, AUXINIT, AUXEXTEND, AUXCLEAR, AUXMOVE) \ + usz log = 64 - CLZ(n); \ + usz msl = (64 - CLZ(n+n/2)) + 1; if (RAD && msl>20) msl=20; \ + usz sh = W - (msl<14? msl : 12+(msl&1)); /* Shift to fit to table */ \ + usz sz = 1 << (W - sh); /* Initial size */ \ + usz msz = 1ull << msl; /* Max sz */ \ + usz b = 64; /* Block size */ \ + /* Resize or abort if more than 1/2^thresh collisions/element */ \ + usz thresh = THRESH; \ + /* Filling e slots past the end requires e*(e+1)/2 collisions, so */ \ + /* n entries with <2 each can fill b? i+b : n; \ + for (; i < e; i++) { \ + T h = hash##W(xp[i]); T j0 = h>>sh, j = j0; T k; \ + while (k=hash[j], k!=h & k!=x0) j++; \ + cc += j-j0; \ + RESWRITE \ + } \ + if (i == n) break; \ + i64 dc = (i64)cc - ((THRESHMUL*i)>>thresh); \ + if (dc >= 0) { \ + if (sz == msz || (RAD && i < n/2 && sz >= 1<<18)) break; /*Abort*/ \ + /* Avoid resizing if close to the end */ \ + if (cc>(5+log-(W-sh))) continue;\ + /* Resize hash, factor of 4 */ \ + usz m = 2; \ + usz dif = sz*((1<>sh, k = k0; while (hash[k]!=x0) k++; \ + cc += k-k0; \ + hash[k] = h; AUXMOVE \ + } \ + if (cc >= STOP) break; \ + thresh = THRESH; \ + } \ + } \ + TFREE(halloc); \ + if (i==n) { decG(x); return RESULT; } B memberOf_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("∊: Argument cannot have rank 0"); @@ -123,66 +178,16 @@ B memberOf_c1(B t, B x) { if (lw == 3) { if (n<8) { BRUTE(8); } else { LOOKUP(8); } } if (lw == 4) { if (n<8) { BRUTE(16); } else { LOOKUP(16); } } #undef LOOKUP - #define HASHTAB(T, W, RAD, STOP, THRESH) \ - T* xp = (T*)xv; \ - usz log = 64 - CLZ(n); \ - usz msl = (64 - CLZ(n+n/2)) + 1; if (RAD && msl>20) msl=20; \ - usz sh = W - (msl<14? msl : 12+(msl&1)); /* Shift to fit to table */ \ - usz sz = 1 << (W - sh); /* Initial size */ \ - usz msz = 1 << msl; /* Max sz */ \ - usz b = 64; /* Block size */ \ - /* Resize or abort if more than 1/2^thresh collisions/element */ \ - usz thresh = THRESH; \ - /* Filling e slots past the end requires e*(e+1)/2 collisions, so */ \ - /* n entries with <2 each can fill b? i+b : n; \ - for (; i < e; i++) { \ - T h = hash##W(xp[i]); T j0 = h>>sh, j = j0; T k; \ - while (k=hash[j], k!=h & k!=x0) j++; \ - cc += j-j0; \ - hash[j] = h; \ - rp[i] = k != h; \ - } \ - if (i == n) break; \ - i64 dc = (i64)cc - (i>>thresh); \ - if (dc >= 0) { \ - if (sz == msz || (RAD && i < n/2 && sz >= 1<<18)) break; /*Abort*/ \ - /* Avoid resizing if close to the end */ \ - if (cc>(5+log-(W-sh))) continue;\ - /* Resize hash, factor of 4 */ \ - usz m = 2; \ - usz dif = sz*((1<>sh, k = k0; while (hash[k]!=x0) k++; \ - cc += k-k0; \ - hash[k] = h; \ - } \ - if (cc >= STOP) break; \ - thresh = THRESH; \ - } \ - } \ - TFREE(hash0); \ - if (i==n) { decG(x); return taga(cpyBitArr(r)); } + #define HASHTAB(T, W, RAD, STOP, THRESH) T* xp = (T*)xv; SELFHASHTAB( \ + T, W, RAD, STOP, \ + 1, taga(cpyBitArr(r)), hash[j]=h; rp[i]=k!=h;, \ + 1, THRESH, 0,,,,) if (lw == 5) { if (n<=12) { BRUTE(32); } - - // Resizable hash table, fall back to radix lookup i8* rp; B r = m_i8arrv(&rp, n); HASHTAB(u32, 32, 1, n/2, sz==msz? 1 : sz>=(1<<15)? 3 : 5) - // Radix-assisted lookup + // Radix-assisted lookup when hash table gives up usz rx = 256, tn = 1<<16; // Radix; table length u32* v0 = (u32*)xv; i8* r0 = rp; @@ -204,7 +209,7 @@ B memberOf_c1(B t, B x) { return taga(cpyBitArr(r)); } if (lw == 6 && canCompare64_norm(x, n)) { - if (n<=12) { BRUTE(64); } + if (n<=20) { BRUTE(64); } i8* rp; B r = m_i8arrv(&rp, n); HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5) decG(r); // Fall through @@ -332,6 +337,19 @@ B indexOf_c1(B t, B x) { if (lw==4) { if (n<12) { BRUTE(16); } else { LOOKUP(16); } } #undef LOOKUP + #define HASHTAB(T, W, THRESH) SELFHASHTAB(T, W, 0, 2*n, \ + /*RES0*/0, /*RESULT*/r, \ + /* RESWRITE */ \ + if (k!=h) { val[j]=ctr++; hash[j]=h; } rp[i]=val[j]; , \ + /*THRESHMUL*/2, THRESH, \ + /*AUXSIZE*/sizeof(usz), \ + /* AUXINIT */ \ + usz* val = (usz*)(hash+sz+ext) + msz-sz; \ + for (usz i = 0; i < sz+ext; i++) val[i] = 0; \ + usz ctr = 1; , \ + /* AUXEXTEND */ \ + val -= dif; for (j = 0; j < dif; j++) val[j] = 0; , \ + /*AUXCLEAR*/val[j] = 0;, /*AUXMOVE*/val[k] = val[j];) if (lw==5) { if (n<12) { BRUTE(32); } B r; @@ -349,26 +367,20 @@ B indexOf_c1(B t, B x) { for (i64 i = 0; i < dst; i++) tmp[i] = n; DOTAB(i32) TFREE(tmp); - } else { - u64 sz = 2ull << (64 - CLZ(n+n/2)); - TALLOC(u32, hash, 2*sz); u32* val = hash + sz; - u32 mask = sz-1; - u32 x0 = hash32(xp[0]), h0 = x0&mask; rp[0] = 0; - for (usz j=h0, e=szsz) for (usz j=0; j=(1<<18)? 1 : sz>=(1<<14)? 4 : 6) + decG(r); // Fall through } + if (lw==6 && canCompare64_norm(x, n)) { + if (n<12) { BRUTE(64); } + i32* rp; B r = m_i32arrv(&rp, n); + u64* xp = tyany_ptr(x); + HASHTAB(u64, 64, sz==msz? 0 : sz>=(1<<17)? 1 : sz>=(1<<13)? 4 : 6) + decG(r); // Fall through + } + #undef HASHTAB #undef BRUTE #undef DOTAB From 8560ce0f4dac64ed4cf4def137f4d1d4e5d419e0 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 6 Nov 2022 12:37:21 -0500 Subject: [PATCH 07/11] Move hash table initializers to functions to save some binary space --- src/builtins/selfsearch.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 2ef284d6..ab751d20 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -87,6 +87,9 @@ u8 radix_offsets_2_u32(usz* c0, u32* v0, usz n) { } \ decG(x); TFREE(alloc); +static NOINLINE void memset32(u32* p, u32 v, usz l) { for (usz i=0; i>sh, k = k0; while (hash[k]!=x0) k++; \ cc += k-k0; \ @@ -342,13 +345,13 @@ B indexOf_c1(B t, B x) { /* RESWRITE */ \ if (k!=h) { val[j]=ctr++; hash[j]=h; } rp[i]=val[j]; , \ /*THRESHMUL*/2, THRESH, \ - /*AUXSIZE*/sizeof(usz), \ + /*AUXSIZE*/sizeof(u32), \ /* AUXINIT */ \ - usz* val = (usz*)(hash+sz+ext) + msz-sz; \ - for (usz i = 0; i < sz+ext; i++) val[i] = 0; \ - usz ctr = 1; , \ + u32* val = (u32*)(hash+sz+ext) + msz-sz; \ + memset32(val, 0, sz+ext); \ + u32 ctr = 1; , \ /* AUXEXTEND */ \ - val -= dif; for (j = 0; j < dif; j++) val[j] = 0; , \ + val -= dif; memset32(val, 0, dif); , \ /*AUXCLEAR*/val[j] = 0;, /*AUXMOVE*/val[k] = val[j];) if (lw==5) { if (n<12) { BRUTE(32); } From 62867f45332610cc78d5cb92bcdd51096be456c4 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 6 Nov 2022 12:56:00 -0500 Subject: [PATCH 08/11] Make brute-force Mark Firsts store result in a register (always <64 elements) --- src/builtins/selfsearch.c | 53 ++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index ab751d20..31fd61c8 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -160,14 +160,15 @@ B memberOf_c1(B t, B x) { return r; } #define BRUTE(T) \ - i##T* xp = xv; \ - u64* rp; B r = m_bitarrv(&rp, n); bitp_set(rp, 0, 1); \ - for (usz i=1; i Date: Sun, 6 Nov 2022 16:04:24 -0500 Subject: [PATCH 09/11] =?UTF-8?q?Fix=20value=20movement=20when=20resizing?= =?UTF-8?q?=20Classify=20(=E2=8A=90)=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 31fd61c8..2e0a1d0f 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -91,7 +91,7 @@ static NOINLINE void memset32(u32* p, u32 v, usz l) { for (usz i=0; i20) msl=20; \ usz sh = W - (msl<14? msl : 12+(msl&1)); /* Shift to fit to table */ \ @@ -133,7 +133,7 @@ static NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i>sh, k = k0; while (hash[k]!=x0) k++; \ cc += k-k0; \ hash[k] = h; AUXMOVE \ @@ -185,9 +185,9 @@ B memberOf_c1(B t, B x) { #define HASHTAB(T, W, RAD, STOP, THRESH) T* xp = (T*)xv; SELFHASHTAB( \ T, W, RAD, STOP, \ 1, taga(cpyBitArr(r)), hash[j]=h; rp[i]=k!=h;, \ - 1, THRESH, 0,,,,) + 1, THRESH, 0,,,) if (lw == 5) { - if (n<=12) { BRUTE(32); } + if (n<12) { BRUTE(32); } i8* rp; B r = m_i8arrv(&rp, n); HASHTAB(u32, 32, 1, n/2, sz==msz? 1 : sz>=(1<<15)? 3 : 5) @@ -213,7 +213,7 @@ B memberOf_c1(B t, B x) { return taga(cpyBitArr(r)); } if (lw == 6 && canCompare64_norm(x, n)) { - if (n<=20) { BRUTE(64); } + if (n<20) { BRUTE(64); } i8* rp; B r = m_i8arrv(&rp, n); HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5) decG(r); // Fall through @@ -351,11 +351,10 @@ B indexOf_c1(B t, B x) { u32* val = (u32*)(hash+sz+ext) + msz-sz; \ memset32(val, 0, sz+ext); \ u32 ctr = 1; , \ - /* AUXEXTEND */ \ - val -= dif; memset32(val, 0, dif); , \ - /*AUXCLEAR*/val[j] = 0;, /*AUXMOVE*/val[k] = val[j];) + /*AUXEXTEND*/val -= dif; memset32(val, 0, dif); , \ + /*AUXMOVE*/u32 v = val[j]; val[j] = 0; val[k] = v;) if (lw==5) { - if (n<16) { BRUTE(32); } + if (n<12) { BRUTE(32); } B r; i32* rp; r = m_i32arrv(&rp, n); i32* xp = tyany_ptr(x); @@ -378,7 +377,7 @@ B indexOf_c1(B t, B x) { decG(r); // Fall through } if (lw==6 && canCompare64_norm(x, n)) { - if (n<12) { BRUTE(64); } + if (n<16) { BRUTE(64); } i32* rp; B r = m_i32arrv(&rp, n); u64* xp = tyany_ptr(x); HASHTAB(u64, 64, sz==msz? 0 : sz>=(1<<17)? 1 : sz>=(1<<13)? 4 : 6) From 7d56d6123594813777b7cfa6b177bcab34b7b463 Mon Sep 17 00:00:00 2001 From: Marshall Lochbaum Date: Sun, 6 Nov 2022 16:05:13 -0500 Subject: [PATCH 10/11] =?UTF-8?q?Hash=20table=20for=20Occurrence=20Count?= =?UTF-8?q?=20(=E2=8A=92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 40 +++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index 2e0a1d0f..cde95793 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -144,6 +144,15 @@ static NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i=(1<<14)? 3 : 5) // Radix-assisted lookup usz rx = 256, tn = 1<<16; // Radix; table length u32* v0 = (u32*)xv; - i32* r0; B r = m_i32arrv(&r0, n); + i32* r0 = rp; TALLOC(u8, alloc, 6*n+(4+4*(tn>n?tn:n)+(2*rx+1)*sizeof(usz))); // timeline @@ -284,6 +303,13 @@ B count_c1(B t, B x) { RADIX_LOOKUP_32(0, ++) return num_squeeze(r); } + if (lw == 6 && canCompare64_norm(x, n)) { + if (n<20) { BRUTE(64); } + i32* rp; B r = m_i32arrv(&rp, n); + HASHTAB(u64, 64, 0, n, sz==msz? 0 : sz>=(1<<18)? 0 : sz>=(1<<14)? 3 : 5) + decG(r); // Fall through + } + #undef HASHTAB #undef BRUTE if (RNK(x)>1) x = toCells(x); @@ -341,18 +367,12 @@ B indexOf_c1(B t, B x) { if (lw==4) { if (n<12) { BRUTE(16); } else { LOOKUP(16); } } #undef LOOKUP - #define HASHTAB(T, W, THRESH) SELFHASHTAB(T, W, 0, 2*n, \ + #define HASHTAB(T, W, THRESH) SELFHASHTAB_VAL(T, W, 0, 2*n, \ /*RES0*/0, /*RESULT*/r, \ /* RESWRITE */ \ if (k!=h) { val[j]=ctr++; hash[j]=h; } rp[i]=val[j]; , \ /*THRESHMUL*/2, THRESH, \ - /*AUXSIZE*/sizeof(u32), \ - /* AUXINIT */ \ - u32* val = (u32*)(hash+sz+ext) + msz-sz; \ - memset32(val, 0, sz+ext); \ - u32 ctr = 1; , \ - /*AUXEXTEND*/val -= dif; memset32(val, 0, dif); , \ - /*AUXMOVE*/u32 v = val[j]; val[j] = 0; val[k] = v;) + /*INIT*/u32 ctr = 1;) if (lw==5) { if (n<12) { BRUTE(32); } B r; From b244d7ecf565dbd91ae71250877769124a4652f9 Mon Sep 17 00:00:00 2001 From: dzaima Date: Sat, 12 Nov 2022 23:43:45 +0200 Subject: [PATCH 11/11] =?UTF-8?q?disallow=20bitwise=20hashing=20for=20?= =?UTF-8?q?=C2=AF0;=20prepare=20for=20mm=5Falloc=20taking=20u64=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/builtins/selfsearch.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/builtins/selfsearch.c b/src/builtins/selfsearch.c index cde95793..bc5aa5ef 100644 --- a/src/builtins/selfsearch.c +++ b/src/builtins/selfsearch.c @@ -28,11 +28,12 @@ static bool canCompare64_norm(B x, usz n) { u8 e = TI(x,elType); if (e == el_B) return 0; if (e == el_f64) { - f64* p = f64any_ptr(x); + f64* pf = f64any_ptr(x); + u64* pu = (u64*)pf; for (usz i = 0; i < n; i++) { - f64 v = p[i]; - if (v!=v) return 0; - if (v==0) p[i]=0; + f64 f = pf[i]; + if (f!=f) return 0; + if (pu[i] == m_f64(-0.0).u) return 0; } } return 1; @@ -156,7 +157,7 @@ static NOINLINE void memset64(u64* p, u64 v, usz l) { for (usz i=0; i(usz)I32_MAX+1) thrM("⊒: Argument length >2⋆31 not supported"); @@ -326,7 +327,7 @@ B count_c1(B t, B x) { B indexOf_c1(B t, B x) { if (isAtm(x) || RNK(x)==0) thrM("⊐: 𝕩 cannot have rank 0"); - usz n = *SH(x); + u64 n = *SH(x); if (n<=1) { decG(x); return n ? taga(arr_shVec(allZeroes(1))) : emptyIVec(); } if (n>(usz)I32_MAX+1) thrM("⊐: Argument length >2⋆31 not supported");