Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * - Hash Table Creation
15 : * The hash indexing scheme for BATs reserves a block of memory to
16 : * maintain the hash table and a collision list. A one-to-one mapping
17 : * exists between the BAT and the collision list using the BUN
18 : * index. NOTE: we alloc the link list as a parallel array to the BUN
19 : * array; hence the hash link array has the same size as
20 : * BATcapacity(b) (not BATcount(b)). This allows us in the BUN insert
21 : * and delete to assume that there is hash space if there is BUN
22 : * space.
23 : *
24 : * The hash mask size is a power of two, so we can do bitwise AND on
25 : * the hash (integer) number to quickly find the head of the bucket
26 : * chain. Clearly, the hash mask size is a crucial parameter. If we
27 : * know that the column is unique (tkey), we use direct hashing (mask
28 : * size ~= BATcount). Otherwise we dynamically determine the mask size
29 : * by starting out with mask size = BATcount/64 (just 1.5% of memory
30 : * storage overhead). Then we start building the hash table on the
31 : * first 25% of the BAT. As we aim for max-collisions list length of
32 : * 4, the list on 25% should not exceed length 1. So, if a small
33 : * number of collisssions occurs (mask/2) then we abandon the attempt
34 : * and restart with a mask that is 4 times larger. This converges
35 : * after three cycles to direct hashing.
36 : */
37 :
38 : #include "monetdb_config.h"
39 : #include "gdk.h"
40 : #include "gdk_private.h"
41 :
42 : static inline uint8_t __attribute__((__const__))
43 1354096 : HASHwidth(BUN hashsize)
44 : {
45 1354096 : (void) hashsize;
46 : #ifdef BUN2
47 1354096 : if (hashsize <= (BUN) BUN2_NONE)
48 : return BUN2;
49 : #endif
50 : #ifdef BUN8
51 514 : if (hashsize > (BUN) BUN4_NONE)
52 0 : return BUN8;
53 : #endif
54 : return BUN4;
55 : }
56 :
57 : static inline BUN __attribute__((__const__))
58 8862 : hashmask(BUN m)
59 : {
60 8862 : m |= m >> 1;
61 8862 : m |= m >> 2;
62 8862 : m |= m >> 4;
63 8862 : m |= m >> 8;
64 8862 : m |= m >> 16;
65 : #if SIZEOF_BUN == 8
66 8862 : m |= m >> 32;
67 : #endif
68 8862 : return m;
69 : }
70 :
71 : static inline void
72 863592 : HASHclear(Hash *h)
73 : {
74 : /* since BUN2_NONE, BUN4_NONE, BUN8_NONE
75 : * are all equal to ~0, i.e., have all bits set,
76 : * we can use a simple memset() to clear the Hash,
77 : * rather than iteratively assigning individual
78 : * BUNi_NONE values in a for-loop
79 : */
80 863592 : memset(h->Bckt, 0xFF, h->nbucket * h->width);
81 863592 : }
82 :
83 : #define HASH_VERSION 6
84 : /* this is only for the change of hash function of the floating point
85 : * types, the UUID type and the MBR type; if HASH_VERSION is increased
86 : * again from 6, the code associated with HASH_VERSION_NOUUID and
87 : * HASH_VERSION_NOMBR must be deleted */
88 : #define HASH_VERSION_FLOAT 5
89 : #define HASH_VERSION_NOMBR 4
90 : #define HASH_VERSION_NOUUID 3
91 : #define HASH_HEADER_SIZE 7 /* nr of size_t fields in header */
92 :
93 : void
94 19169449 : doHASHdestroy(BAT *b, Hash *hs)
95 : {
96 19169449 : if (hs == (Hash *) 1) {
97 15 : GDKunlink(BBPselectfarm(b->batRole, b->ttype, hashheap),
98 : BATDIR,
99 15 : BBP_physical(b->batCacheid),
100 : "thashl");
101 15 : GDKunlink(BBPselectfarm(b->batRole, b->ttype, hashheap),
102 : BATDIR,
103 15 : BBP_physical(b->batCacheid),
104 : "thashb");
105 19169434 : } else if (hs) {
106 11454 : TRC_DEBUG(ACCELERATOR, ALGOBATFMT ": removing%s hash\n", ALGOBATPAR(b), *(size_t *) hs->heapbckt.base & (1 << 24) ? " persisted" : "");
107 11454 : HEAPfree(&hs->heapbckt, true);
108 11454 : HEAPfree(&hs->heaplink, true);
109 11454 : GDKfree(hs);
110 : }
111 19169449 : }
112 :
113 : gdk_return
114 863592 : HASHnew(Hash *h, int tpe, BUN size, BUN mask, BUN count, bool bcktonly)
115 : {
116 863592 : if (h->width == 0)
117 850852 : h->width = HASHwidth(size);
118 :
119 863592 : if (!bcktonly) {
120 850383 : if (HEAPalloc(&h->heaplink, size, h->width) != GDK_SUCCEED)
121 : return GDK_FAIL;
122 850385 : h->heaplink.free = size * h->width;
123 850385 : h->heaplink.dirty = true;
124 850385 : h->Link = h->heaplink.base;
125 : }
126 863594 : if (HEAPalloc(&h->heapbckt, mask + HASH_HEADER_SIZE * SIZEOF_SIZE_T / h->width, h->width) != GDK_SUCCEED) {
127 0 : if (!bcktonly) {
128 0 : HEAPfree(&h->heaplink, true);
129 0 : h->heaplink.free = 0;
130 0 : h->Link = NULL;
131 : }
132 0 : return GDK_FAIL;
133 : }
134 863592 : h->heapbckt.free = mask * h->width + HASH_HEADER_SIZE * SIZEOF_SIZE_T;
135 863592 : h->heapbckt.dirty = true;
136 863592 : h->nbucket = mask;
137 863592 : if (mask & (mask - 1)) {
138 8695 : h->mask2 = hashmask(mask);
139 8695 : h->mask1 = h->mask2 >> 1;
140 : } else {
141 : /* mask is a power of two */
142 854897 : h->mask1 = mask - 1;
143 854897 : h->mask2 = h->mask1 << 1 | 1;
144 : }
145 863592 : h->Bckt = h->heapbckt.base + HASH_HEADER_SIZE * SIZEOF_SIZE_T;
146 863592 : h->type = tpe;
147 863592 : HASHclear(h); /* zero the mask */
148 863589 : ((size_t *) h->heapbckt.base)[0] = (size_t) HASH_VERSION;
149 863589 : ((size_t *) h->heapbckt.base)[1] = (size_t) size;
150 863589 : ((size_t *) h->heapbckt.base)[2] = (size_t) h->nbucket;
151 863589 : ((size_t *) h->heapbckt.base)[3] = (size_t) h->width;
152 863589 : ((size_t *) h->heapbckt.base)[4] = (size_t) count;
153 863589 : ((size_t *) h->heapbckt.base)[5] = (size_t) h->nunique;
154 863589 : ((size_t *) h->heapbckt.base)[6] = (size_t) h->nheads;
155 863589 : TRC_DEBUG(ACCELERATOR,
156 : "create hash(size " BUNFMT ", mask " BUNFMT ", width %d, total " BUNFMT " bytes);\n", size, mask, h->width, (size + mask) * h->width);
157 : return GDK_SUCCEED;
158 : }
159 :
160 : /* collect HASH statistics for analysis */
161 : static void
162 0 : HASHcollisions(BAT *b, Hash *h, const char *func)
163 : {
164 0 : lng cnt, entries = 0, max = 0;
165 0 : double total = 0;
166 0 : BUN p, i, j;
167 :
168 0 : if (b == 0 || h == 0)
169 : return;
170 0 : for (i = 0, j = h->nbucket; i < j; i++)
171 0 : if ((p = HASHget(h, i)) != BUN_NONE) {
172 0 : entries++;
173 0 : cnt = 0;
174 0 : for (; p != BUN_NONE; p = HASHgetlink(h, p))
175 0 : cnt++;
176 0 : if (cnt > max)
177 : max = cnt;
178 0 : total += cnt;
179 : }
180 0 : TRC_DEBUG_ENDIF(ACCELERATOR,
181 : "%s(" ALGOBATFMT "): statistics " BUNFMT ", "
182 : "entries " LLFMT ", nunique " BUNFMT ", "
183 : "nbucket " BUNFMT ", max " LLFMT ", avg %2.6f;\n",
184 : func, ALGOBATPAR(b), BATcount(b), entries,
185 : h->nunique, h->nbucket, max,
186 : entries == 0 ? 0 : total / entries);
187 : }
188 :
189 : static gdk_return
190 0 : HASHupgradehashheap(BAT *b)
191 : {
192 : #if defined(BUN2) || defined(BUN8)
193 0 : Hash *h = b->thash;
194 0 : int nwidth = h->width << 1;
195 0 : BUN i;
196 :
197 0 : assert(nwidth <= SIZEOF_BUN);
198 0 : assert((nwidth & (nwidth - 1)) == 0);
199 :
200 0 : if (HEAPextend(&h->heaplink, h->heaplink.size * nwidth / h->width, true) != GDK_SUCCEED ||
201 0 : HEAPextend(&h->heapbckt, (h->heapbckt.size - HASH_HEADER_SIZE * SIZEOF_SIZE_T) * nwidth / h->width + HASH_HEADER_SIZE * SIZEOF_SIZE_T, true) != GDK_SUCCEED) {
202 0 : b->thash = NULL;
203 0 : doHASHdestroy(b, h);
204 0 : return GDK_FAIL;
205 : }
206 0 : h->Link = h->heaplink.base;
207 0 : h->Bckt = h->heapbckt.base + HASH_HEADER_SIZE * SIZEOF_SIZE_T;
208 0 : switch (nwidth) {
209 0 : case BUN4:
210 : #ifdef BUN2
211 0 : switch (h->width) {
212 0 : case BUN2:
213 0 : i = h->heaplink.free / h->width;
214 0 : h->heaplink.free = i * nwidth;
215 0 : while (i > 0) {
216 0 : i--;
217 0 : BUN2type v = ((BUN2type *) h->Link)[i];
218 0 : ((BUN4type *) h->Link)[i] = v == BUN2_NONE ? BUN4_NONE : v;
219 : }
220 0 : i = (h->heapbckt.free - HASH_HEADER_SIZE * SIZEOF_SIZE_T) / h->width;
221 0 : h->heapbckt.free = HASH_HEADER_SIZE * SIZEOF_SIZE_T + i * nwidth;
222 0 : while (i > 0) {
223 0 : i--;
224 0 : BUN2type v = ((BUN2type *) h->Bckt)[i];
225 0 : ((BUN4type *) h->Bckt)[i] = v == BUN2_NONE ? BUN4_NONE : v;
226 : }
227 0 : h->heapbckt.dirty = true;
228 0 : h->heaplink.dirty = true;
229 0 : break;
230 : }
231 : #endif
232 : break;
233 : #ifdef BUN8
234 0 : case BUN8:
235 0 : switch (h->width) {
236 : #ifdef BUN2
237 0 : case BUN2:
238 0 : i = h->heaplink.free / h->width;
239 0 : h->heaplink.free = i * nwidth;
240 0 : while (i > 0) {
241 0 : i--;
242 0 : BUN2type v = ((BUN2type *) h->Link)[i];
243 0 : ((BUN8type *) h->Link)[i] = v == BUN2_NONE ? BUN8_NONE : v;
244 : }
245 0 : i = (h->heapbckt.free - HASH_HEADER_SIZE * SIZEOF_SIZE_T) / h->width;
246 0 : h->heapbckt.free = HASH_HEADER_SIZE * SIZEOF_SIZE_T + i * nwidth;
247 0 : while (i > 0) {
248 0 : i--;
249 0 : BUN2type v = ((BUN2type *) h->Bckt)[i];
250 0 : ((BUN8type *) h->Bckt)[i] = v == BUN2_NONE ? BUN8_NONE : v;
251 : }
252 0 : h->heapbckt.dirty = true;
253 0 : h->heaplink.dirty = true;
254 0 : break;
255 : #endif
256 0 : case BUN4:
257 0 : i = h->heaplink.free / h->width;
258 0 : h->heaplink.free = i * nwidth;
259 0 : while (i > 0) {
260 0 : i--;
261 0 : BUN4type v = ((BUN4type *) h->Link)[i];
262 0 : ((BUN8type *) h->Link)[i] = v == BUN4_NONE ? BUN8_NONE : v;
263 : }
264 0 : i = (h->heapbckt.free - HASH_HEADER_SIZE * SIZEOF_SIZE_T) / h->width;
265 0 : h->heapbckt.free = HASH_HEADER_SIZE * SIZEOF_SIZE_T + i * nwidth;
266 0 : while (i > 0) {
267 0 : i--;
268 0 : BUN4type v = ((BUN4type *) h->Bckt)[i];
269 0 : ((BUN8type *) h->Bckt)[i] = v == BUN4_NONE ? BUN8_NONE : v;
270 : }
271 0 : h->heapbckt.dirty = true;
272 0 : h->heaplink.dirty = true;
273 0 : break;
274 : }
275 : break;
276 : #endif
277 : }
278 0 : h->width = nwidth;
279 : #else
280 : (void) b;
281 : #endif
282 0 : return GDK_SUCCEED;
283 : }
284 :
285 : /* write/remove the bit into/from the hash file that indicates the hash
286 : * is good to go; the bit is the last part to be written and the first
287 : * to be removed */
288 : static inline gdk_return
289 542935 : HASHfix(Hash *h, bool save, bool dosync)
290 : {
291 542935 : if (!h->heapbckt.dirty && !h->heaplink.dirty) {
292 24368 : const size_t mask = (size_t) 1 << 24;
293 24368 : if (((size_t *) h->heapbckt.base)[0] & mask) {
294 10716 : if (save)
295 : return GDK_SUCCEED;
296 10716 : ((size_t *) h->heapbckt.base)[0] &= ~mask;
297 : } else {
298 13652 : if (!save)
299 : return GDK_SUCCEED;
300 13652 : ((size_t *) h->heapbckt.base)[0] |= mask;
301 : }
302 24368 : if (h->heapbckt.storage == STORE_MEM) {
303 24344 : gdk_return rc = GDK_FAIL;
304 24344 : int fd = GDKfdlocate(h->heapbckt.farmid, h->heapbckt.filename, "rb+", NULL);
305 24344 : if (fd >= 0) {
306 24344 : if (write(fd, h->heapbckt.base, SIZEOF_SIZE_T) == SIZEOF_SIZE_T) {
307 24344 : if (dosync &&
308 24344 : !(ATOMIC_GET(&GDKdebug) & NOSYNCMASK)) {
309 : #if defined(NATIVE_WIN32)
310 : _commit(fd);
311 : #elif defined(HAVE_FDATASYNC)
312 46 : fdatasync(fd);
313 : #elif defined(HAVE_FSYNC)
314 : fsync(fd);
315 : #endif
316 : }
317 : rc = GDK_SUCCEED;
318 : }
319 24344 : close(fd);
320 : }
321 24344 : if (rc != GDK_SUCCEED)
322 0 : ((size_t *) h->heapbckt.base)[0] &= ~mask;
323 24344 : return rc;
324 : } else {
325 24 : if (dosync &&
326 47 : !(ATOMIC_GET(&GDKdebug) & NOSYNCMASK) &&
327 23 : MT_msync(h->heapbckt.base, SIZEOF_SIZE_T) < 0) {
328 0 : ((size_t *) h->heapbckt.base)[0] &= ~mask;
329 0 : return GDK_FAIL;
330 : }
331 : }
332 : }
333 : return GDK_SUCCEED;
334 : }
335 :
336 : static gdk_return
337 490502 : HASHgrowbucket(BAT *b)
338 : {
339 490502 : Hash *h = b->thash;
340 490502 : BUN nbucket;
341 490502 : BUN onbucket = h->nbucket;
342 490502 : lng t0 = 0;
343 :
344 490502 : TRC_DEBUG_IF(ACCELERATOR) t0 = GDKusec();
345 :
346 : /* only needed to fix hash tables built before this fix was
347 : * introduced */
348 490502 : if (h->width < SIZEOF_BUN &&
349 490502 : ((BUN) 1 << (h->width * 8)) - 1 <= h->mask2 &&
350 0 : HASHupgradehashheap(b) != GDK_SUCCEED)
351 : return GDK_FAIL;
352 :
353 490502 : h->heapbckt.dirty = true;
354 490502 : h->heaplink.dirty = true;
355 747996 : while (h->nunique >= (nbucket = h->nbucket) * 7 / 8) {
356 257494 : BUN new = h->nbucket;
357 257494 : BUN old = new & h->mask1;
358 257494 : BUN mask = h->mask1 + 1; /* == h->mask2 - h->mask1 */
359 :
360 257494 : assert(h->heapbckt.free == nbucket * h->width + HASH_HEADER_SIZE * SIZEOF_SIZE_T);
361 257494 : if (h->heapbckt.free + h->width > h->heapbckt.size) {
362 4722 : if (HEAPextend(&h->heapbckt,
363 : h->heapbckt.size + GDK_mmap_pagesize,
364 : true) != GDK_SUCCEED) {
365 0 : return GDK_FAIL;
366 : }
367 4722 : h->Bckt = h->heapbckt.base + HASH_HEADER_SIZE * SIZEOF_SIZE_T;
368 : }
369 257494 : assert(h->heapbckt.free + h->width <= h->heapbckt.size);
370 257494 : if (h->nbucket == h->mask2) {
371 477 : h->mask1 = h->mask2;
372 477 : h->mask2 |= h->mask2 << 1;
373 477 : if (h->width < SIZEOF_BUN &&
374 477 : h->mask2 == ((BUN) 1 << (h->width * 8)) - 1) {
375 : /* time to widen the hash table */
376 0 : if (HASHupgradehashheap(b) != GDK_SUCCEED)
377 : return GDK_FAIL;
378 : }
379 : }
380 257494 : h->nbucket++;
381 257494 : h->heapbckt.free += h->width;
382 257494 : BUN lold, lnew, hb;
383 257494 : lold = lnew = BUN_NONE;
384 257494 : BATiter bi = bat_iterator(b);
385 257494 : if ((hb = HASHget(h, old)) != BUN_NONE) {
386 206737 : h->nheads--;
387 334113 : do {
388 334113 : const void *v = BUNtail(bi, hb);
389 334113 : BUN hsh = ATOMhash(h->type, v);
390 334113 : assert((hsh & (mask - 1)) == old);
391 334113 : if (hsh & mask) {
392 : /* move to new list */
393 109311 : if (lnew == BUN_NONE) {
394 98515 : HASHput(h, new, hb);
395 98515 : h->nheads++;
396 : } else
397 10796 : HASHputlink(h, lnew, hb);
398 : lnew = hb;
399 : } else {
400 : /* move to old list */
401 224802 : if (lold == BUN_NONE) {
402 171282 : h->nheads++;
403 171282 : HASHput(h, old, hb);
404 : } else
405 53520 : HASHputlink(h, lold, hb);
406 : lold = hb;
407 : }
408 334113 : hb = HASHgetlink(h, hb);
409 334113 : } while (hb != BUN_NONE);
410 : }
411 257494 : bat_iterator_end(&bi);
412 257494 : if (lnew == BUN_NONE)
413 158979 : HASHput(h, new, BUN_NONE);
414 : else
415 98515 : HASHputlink(h, lnew, BUN_NONE);
416 257494 : if (lold == BUN_NONE)
417 86212 : HASHput(h, old, BUN_NONE);
418 : else
419 171282 : HASHputlink(h, lold, BUN_NONE);
420 : }
421 490502 : TRC_DEBUG_IF(ACCELERATOR) if (h->nbucket > onbucket) {
422 0 : TRC_DEBUG_ENDIF(ACCELERATOR, ALGOBATFMT " " BUNFMT
423 : " -> " BUNFMT " buckets (" LLFMT " usec)\n",
424 : ALGOBATPAR(b),
425 : onbucket, h->nbucket, GDKusec() - t0);
426 0 : HASHcollisions(b, h, __func__);
427 : }
428 : return GDK_SUCCEED;
429 : }
430 :
431 : /* Return TRUE if we have a hash on the tail, even if we need to read
432 : * one from disk.
433 : *
434 : * Note that the b->thash pointer can be NULL, meaning there is no
435 : * hash; (Hash *) 1, meaning there is no hash loaded, but it may exist
436 : * on disk; or a valid pointer to a loaded hash. These values are
437 : * maintained here, in the HASHdestroy and HASHfree functions, and in
438 : * BBPdiskscan during initialization. */
439 : bool
440 63206976 : BATcheckhash(BAT *b)
441 : {
442 63206976 : lng t = 0;
443 63206976 : Hash *h;
444 :
445 63206976 : MT_rwlock_rdlock(&b->thashlock);
446 63217109 : h = b->thash;
447 63217109 : MT_rwlock_rdunlock(&b->thashlock);
448 63216077 : if (h == (Hash *) 1) {
449 : /* but when we want to change it, we need the lock */
450 425 : TRC_DEBUG_IF(ACCELERATOR) t = GDKusec();
451 425 : MT_rwlock_wrlock(&b->thashlock);
452 425 : TRC_DEBUG_IF(ACCELERATOR) t = GDKusec() - t;
453 : /* if still 1 now that we have the lock, we can update */
454 425 : if (b->thash == (Hash *) 1) {
455 425 : int fd;
456 :
457 425 : assert(!GDKinmemory(b->theap->farmid));
458 425 : b->thash = NULL;
459 425 : if ((h = GDKzalloc(sizeof(*h))) != NULL &&
460 425 : (h->heaplink.farmid = BBPselectfarm(b->batRole, b->ttype, hashheap)) >= 0 &&
461 425 : (h->heapbckt.farmid = BBPselectfarm(b->batRole, b->ttype, hashheap)) >= 0) {
462 425 : const char *nme = BBP_physical(b->batCacheid);
463 425 : strconcat_len(h->heaplink.filename,
464 : sizeof(h->heaplink.filename),
465 : nme, ".thashl", NULL);
466 425 : strconcat_len(h->heapbckt.filename,
467 : sizeof(h->heapbckt.filename),
468 : nme, ".thashb", NULL);
469 425 : h->heaplink.storage = STORE_INVALID;
470 425 : h->heaplink.newstorage = STORE_INVALID;
471 425 : h->heapbckt.storage = STORE_INVALID;
472 425 : h->heapbckt.newstorage = STORE_INVALID;
473 :
474 : /* check whether a persisted hash can be found */
475 425 : if ((fd = GDKfdlocate(h->heapbckt.farmid, nme, "rb+", "thashb")) >= 0) {
476 425 : size_t hdata[HASH_HEADER_SIZE];
477 425 : struct stat st;
478 :
479 425 : if (read(fd, hdata, sizeof(hdata)) == sizeof(hdata) &&
480 425 : (hdata[0] == (
481 : #ifdef PERSISTENTHASH
482 : ((size_t) 1 << 24) |
483 : #endif
484 : HASH_VERSION)
485 : #ifdef HASH_VERSION_NOUUID
486 : /* if not uuid, also allow previous version */
487 99 : || (hdata[0] == (
488 : #ifdef PERSISTENTHASH
489 : ((size_t) 1 << 24) |
490 : #endif
491 0 : HASH_VERSION_NOUUID) &&
492 0 : strcmp(ATOMname(b->ttype), "flt") != 0 &&
493 0 : strcmp(ATOMname(b->ttype), "dbl") != 0 &&
494 0 : strcmp(ATOMname(b->ttype), "uuid") != 0 &&
495 0 : strcmp(ATOMname(b->ttype), "mbr") != 0)
496 : #endif
497 : #ifdef HASH_VERSION_NOMBR
498 : /* if not uuid, also allow previous version */
499 99 : || (hdata[0] == (
500 : #ifdef PERSISTENTHASH
501 : ((size_t) 1 << 24) |
502 : #endif
503 96 : HASH_VERSION_NOMBR) &&
504 96 : strcmp(ATOMname(b->ttype), "flt") != 0 &&
505 96 : strcmp(ATOMname(b->ttype), "dbl") != 0 &&
506 96 : strcmp(ATOMname(b->ttype), "mbr") != 0)
507 : #endif
508 : #ifdef HASH_VERSION_FLOAT
509 : /* if not floating point, also allow previous version */
510 3 : || (hdata[0] == (
511 : #ifdef PERSISTENTHASH
512 : ((size_t) 1 << 24) |
513 : #endif
514 3 : HASH_VERSION_FLOAT) &&
515 3 : strcmp(ATOMname(b->ttype), "flt") != 0 &&
516 3 : strcmp(ATOMname(b->ttype), "dbl") != 0)
517 : #endif
518 425 : ) &&
519 425 : hdata[1] > 0 &&
520 : (
521 : #ifdef BUN2
522 425 : hdata[3] == BUN2 ||
523 : #endif
524 : hdata[3] == BUN4
525 : #ifdef BUN8
526 : || hdata[3] == BUN8
527 : #endif
528 425 : ) &&
529 850 : hdata[4] == (size_t) BATcount(b) &&
530 425 : fstat(fd, &st) == 0 &&
531 850 : st.st_size >= (off_t) (h->heapbckt.size = h->heapbckt.free = (h->nbucket = (BUN) hdata[2]) * (BUN) (h->width = (uint8_t) hdata[3]) + HASH_HEADER_SIZE * SIZEOF_SIZE_T) &&
532 425 : close(fd) == 0 &&
533 850 : (fd = GDKfdlocate(h->heaplink.farmid, nme, "rb+", "thashl")) >= 0 &&
534 425 : fstat(fd, &st) == 0 &&
535 425 : st.st_size > 0 &&
536 850 : st.st_size >= (off_t) (h->heaplink.size = h->heaplink.free = hdata[1] * h->width) &&
537 425 : HEAPload(&h->heaplink, nme, "thashl", false) == GDK_SUCCEED) {
538 425 : if (HEAPload(&h->heapbckt, nme, "thashb", false) == GDK_SUCCEED) {
539 425 : if (h->nbucket & (h->nbucket - 1)) {
540 167 : h->mask2 = hashmask(h->nbucket);
541 167 : h->mask1 = h->mask2 >> 1;
542 : } else {
543 258 : h->mask1 = h->nbucket - 1;
544 258 : h->mask2 = h->mask1 << 1 | 1;
545 : }
546 425 : h->nunique = hdata[5];
547 425 : h->nheads = hdata[6];
548 425 : h->type = ATOMtype(b->ttype);
549 425 : if (h->width < SIZEOF_BUN &&
550 425 : ((BUN) 1 << (8 * h->width)) - 1 > h->nbucket) {
551 424 : close(fd);
552 424 : h->Link = h->heaplink.base;
553 424 : h->Bckt = h->heapbckt.base + HASH_HEADER_SIZE * SIZEOF_SIZE_T;
554 424 : h->heaplink.parentid = b->batCacheid;
555 424 : h->heapbckt.parentid = b->batCacheid;
556 424 : h->heaplink.dirty = false;
557 424 : h->heapbckt.dirty = false;
558 424 : b->thash = h;
559 424 : h->heapbckt.hasfile = true;
560 424 : h->heaplink.hasfile = true;
561 424 : TRC_DEBUG(ACCELERATOR,
562 : ALGOBATFMT ": reusing persisted hash\n", ALGOBATPAR(b));
563 424 : MT_rwlock_wrunlock(&b->thashlock);
564 424 : return true;
565 : }
566 : /* if h->nbucket
567 : * equals the
568 : * BUN_NONE
569 : * representation
570 : * for the
571 : * current hash
572 : * width (was
573 : * possible in
574 : * previous
575 : * iterations of
576 : * the code),
577 : * then we can't
578 : * use the hash
579 : * since we
580 : * can't
581 : * distinguish
582 : * between
583 : * end-of-list
584 : * and a valid
585 : * link */
586 1 : HEAPfree(&h->heapbckt, false);
587 : }
588 1 : HEAPfree(&h->heaplink, false);
589 : }
590 1 : close(fd);
591 : /* unlink unusable file */
592 1 : GDKunlink(h->heaplink.farmid, BATDIR, nme, "thashl");
593 1 : GDKunlink(h->heapbckt.farmid, BATDIR, nme, "thashb");
594 1 : h->heapbckt.hasfile = false;
595 1 : h->heaplink.hasfile = false;
596 : }
597 : }
598 1 : GDKfree(h);
599 1 : GDKclrerr(); /* we're not currently interested in errors */
600 : }
601 1 : h = b->thash;
602 1 : MT_rwlock_wrunlock(&b->thashlock);
603 : }
604 63215653 : if (h != NULL) {
605 3710819 : TRC_DEBUG(ACCELERATOR, ALGOBATFMT ": already has hash, waited " LLFMT " usec\n", ALGOBATPAR(b), t);
606 : }
607 63215653 : return h != NULL;
608 : }
609 :
610 : static void
611 14522 : BAThashsave_intern(BAT *b, bool dosync)
612 : {
613 14522 : Hash *h;
614 14522 : lng t0 = 0;
615 :
616 14522 : TRC_DEBUG_IF(ACCELERATOR) t0 = GDKusec();
617 :
618 14522 : if ((h = b->thash) != NULL) {
619 : #ifndef PERSISTENTHASH
620 : /* no need to sync if not persistent */
621 : dosync = false;
622 : #endif
623 :
624 : /* only persist if parent BAT hasn't changed in the
625 : * mean time */
626 14522 : if (!b->theap->dirty &&
627 13652 : ((size_t *) h->heapbckt.base)[1] == BATcount(b) &&
628 27304 : ((size_t *) h->heapbckt.base)[4] == BATcount(b) &&
629 27304 : HEAPsave(&h->heaplink, h->heaplink.filename, NULL, dosync, h->heaplink.free, NULL) == GDK_SUCCEED &&
630 13652 : HEAPsave(&h->heapbckt, h->heapbckt.filename, NULL, dosync, h->heapbckt.free, NULL) == GDK_SUCCEED) {
631 13652 : h->heaplink.dirty = false;
632 13652 : h->heapbckt.dirty = false;
633 13652 : h->heaplink.hasfile = true;
634 13652 : h->heapbckt.hasfile = true;
635 13652 : gdk_return rc = HASHfix(h, true, dosync);
636 13652 : TRC_DEBUG(ACCELERATOR,
637 : ALGOBATFMT ": persisting hash %s%s (" LLFMT " usec)%s\n", ALGOBATPAR(b), h->heapbckt.filename, dosync ? "" : " no sync", GDKusec() - t0, rc == GDK_SUCCEED ? "" : " failed");
638 : }
639 14522 : GDKclrerr();
640 : }
641 14522 : }
642 :
643 : void
644 14522 : BAThashsave(BAT *b, bool dosync)
645 : {
646 14522 : Hash *h = b->thash;
647 14522 : if (h == NULL)
648 : return;
649 14522 : ((size_t *) h->heapbckt.base)[0] = (size_t) HASH_VERSION;
650 14522 : ((size_t *) h->heapbckt.base)[1] = (size_t) (h->heaplink.free / h->width);
651 14522 : ((size_t *) h->heapbckt.base)[2] = (size_t) h->nbucket;
652 14522 : ((size_t *) h->heapbckt.base)[3] = (size_t) h->width;
653 14522 : ((size_t *) h->heapbckt.base)[4] = (size_t) BATcount(b);
654 14522 : ((size_t *) h->heapbckt.base)[5] = (size_t) h->nunique;
655 14522 : ((size_t *) h->heapbckt.base)[6] = (size_t) h->nheads;
656 14522 : BAThashsave_intern(b, dosync);
657 : }
658 :
659 : #define EQbte(a, b) ((a) == (b))
660 : #define EQsht(a, b) ((a) == (b))
661 : #define EQint(a, b) ((a) == (b))
662 : #define EQlng(a, b) ((a) == (b))
663 : #ifdef HAVE_HGE
664 : #define EQhge(a, b) ((a) == (b))
665 : #endif
666 : #define EQflt(a, b) (is_flt_nil(a) ? is_flt_nil(b) : (a) == (b))
667 : #define EQdbl(a, b) (is_dbl_nil(a) ? is_dbl_nil(b) : (a) == (b))
668 : #ifdef HAVE_HGE
669 : #define EQuuid(a, b) ((a).h == (b).h)
670 : #else
671 : #define EQuuid(a, b) (memcmp((a).u, (b).u, UUID_SIZE) == 0)
672 : #endif
673 :
674 : #define starthash(TYPE) \
675 : do { \
676 : const TYPE *restrict v = (const TYPE *) BUNtloc(bi, 0); \
677 : TIMEOUT_LOOP(p, qry_ctx) { \
678 : hget = HASHget(h, c); \
679 : if (hget == BUN_NONE) { \
680 : if (h->nheads == maxslots) \
681 : TIMEOUT_LOOP_BREAK; /* mask too full */ \
682 : h->nheads++; \
683 : h->nunique++; \
684 : } else { \
685 : for (hb = hget; \
686 : hb != BUN_NONE; \
687 : hb = HASHgetlink(h, hb)) { \
688 : if (EQ##TYPE(v[o - b->hseqbase], v[hb])) \
689 : break; \
690 : } \
691 : h->nunique += hb == BUN_NONE; \
692 : } \
693 : HASHputlink(h, p, hget); \
694 : HASHput(h, c, p); \
695 : o = canditer_next(ci); \
696 : } \
697 : TIMEOUT_CHECK(qry_ctx, \
698 : GOTO_LABEL_TIMEOUT_HANDLER(bailout, qry_ctx)); \
699 : } while (0)
700 : #define finishhash(TYPE) \
701 : do { \
702 : const TYPE *restrict v = (const TYPE *) BUNtloc(bi, 0); \
703 : TIMEOUT_LOOP(ci->ncand - p, qry_ctx) { \
704 : c = hash_##TYPE(h, v + o - b->hseqbase); \
705 : hget = HASHget(h, c); \
706 : h->nheads += hget == BUN_NONE; \
707 : if (!hascand) { \
708 : for (hb = hget; \
709 : hb != BUN_NONE; \
710 : hb = HASHgetlink(h, hb)) { \
711 : if (EQ##TYPE(v[o - b->hseqbase], v[hb])) \
712 : break; \
713 : } \
714 : h->nunique += hb == BUN_NONE; \
715 : o = canditer_next_dense(ci); \
716 : } else { \
717 : o = canditer_next(ci); \
718 : } \
719 : HASHputlink(h, p, hget); \
720 : HASHput(h, c, p); \
721 : p++; \
722 : } \
723 : TIMEOUT_CHECK(qry_ctx, \
724 : GOTO_LABEL_TIMEOUT_HANDLER(bailout, qry_ctx)); \
725 : } while (0)
726 :
727 : /* Internal function to create a hash table for the given BAT b.
728 : * If a candidate list s is also given, the hash table is specific for
729 : * the combination of the two: only values from b that are referred to
730 : * by s are included in the hash table, so if a result is found when
731 : * searching the hash table, the result is a candidate. */
732 : Hash *
733 13207 : BAThash_impl(BAT *restrict b, struct canditer *restrict ci, const char *restrict ext)
734 : {
735 13207 : lng t0 = 0;
736 13207 : BUN cnt1;
737 13207 : BUN mask, maxmask = 0;
738 13207 : BUN p, c;
739 13207 : oid o;
740 13207 : BUN hget, hb;
741 13207 : Hash *h = NULL;
742 13207 : const char *nme = GDKinmemory(b->theap->farmid) ? ":memory:" : BBP_physical(b->batCacheid);
743 13207 : BATiter bi = bat_iterator(b);
744 13206 : unsigned int tpe = ATOMbasetype(bi.type);
745 13206 : bool hascand = ci->tpe != cand_dense || ci->ncand != bi.count;
746 :
747 13206 : QryCtx *qry_ctx = MT_thread_get_qry_ctx();
748 :
749 13207 : assert(strcmp(ext, "thash") != 0 || !hascand);
750 13207 : assert(bi.type != TYPE_msk);
751 :
752 26243 : MT_thread_setalgorithm(hascand ? "create hash with candidates" : "create hash");
753 13206 : TRC_DEBUG_IF(ACCELERATOR) t0 = GDKusec();
754 13206 : TRC_DEBUG(ACCELERATOR,
755 : ALGOBATFMT ": create hash;\n", ALGOBATPAR(b));
756 13207 : if (bi.type == TYPE_void) {
757 0 : if (is_oid_nil(b->tseqbase)) {
758 0 : TRC_DEBUG(ACCELERATOR,
759 : "cannot create hash-table on void-NIL column.\n");
760 0 : GDKerror("no hash on void/nil column\n");
761 0 : bat_iterator_end(&bi);
762 0 : return NULL;
763 : }
764 0 : TRC_DEBUG(ACCELERATOR,
765 : "creating hash-table on void column..\n");
766 0 : assert(0);
767 : tpe = TYPE_void;
768 : }
769 :
770 13207 : if ((h = GDKzalloc(sizeof(*h))) == NULL ||
771 13207 : (h->heaplink.farmid = BBPselectfarm(hascand ? TRANSIENT : b->batRole, bi.type, hashheap)) < 0 ||
772 13207 : (h->heapbckt.farmid = BBPselectfarm(hascand ? TRANSIENT : b->batRole, bi.type, hashheap)) < 0) {
773 0 : GDKfree(h);
774 0 : bat_iterator_end(&bi);
775 0 : return NULL;
776 : }
777 13206 : h->width = HASHwidth(BATcapacity(b));
778 13206 : h->heaplink.dirty = true;
779 13206 : h->heapbckt.dirty = true;
780 13206 : strconcat_len(h->heaplink.filename, sizeof(h->heaplink.filename),
781 : nme, ".", ext, "l", NULL);
782 13207 : strconcat_len(h->heapbckt.filename, sizeof(h->heapbckt.filename),
783 : nme, ".", ext, "b", NULL);
784 13207 : h->heapbckt.parentid = b->batCacheid;
785 13207 : h->heaplink.parentid = b->batCacheid;
786 13207 : if (HEAPalloc(&h->heaplink, hascand ? ci->ncand : BATcapacity(b),
787 13207 : h->width) != GDK_SUCCEED) {
788 0 : GDKfree(h);
789 0 : bat_iterator_end(&bi);
790 0 : return NULL;
791 : }
792 13207 : h->heaplink.free = ci->ncand * h->width;
793 13207 : h->Link = h->heaplink.base;
794 : #ifndef NDEBUG
795 : /* clear unused part of Link array */
796 13207 : if (h->heaplink.size > h->heaplink.free)
797 12131 : memset(h->heaplink.base + h->heaplink.free, 0,
798 : h->heaplink.size - h->heaplink.free);
799 : #endif
800 :
801 : /* determine hash mask size */
802 13207 : cnt1 = 0;
803 13207 : if (ATOMsize(tpe) == 1) {
804 : /* perfect hash for one-byte sized atoms */
805 : mask = (1 << 8);
806 13197 : } else if (ATOMsize(tpe) == 2) {
807 : /* perfect hash for two-byte sized atoms */
808 : mask = (1 << 16);
809 13141 : } else if (bi.key || ci->ncand <= 4096) {
810 : /* if key, or if small, don't bother dynamically
811 : * adjusting the hash mask */
812 12970 : mask = HASHmask(ci->ncand);
813 171 : } else if (!hascand && bi.unique_est != 0) {
814 171 : mask = (BUN) (bi.unique_est * 1.15); /* about 8/7 */
815 : } else {
816 : /* dynamic hash: we start with HASHmask(ci->ncand)/64, or,
817 : * if ci->ncand large enough, HASHmask(ci->ncand)/256; if there
818 : * are too many collisions we try HASHmask(ci->ncand)/64,
819 : * HASHmask(ci->ncand)/16, HASHmask(ci->ncand)/4, and finally
820 : * HASHmask(ci->ncand), but we might skip some of these if
821 : * there are many distinct values. */
822 0 : maxmask = HASHmask(ci->ncand);
823 0 : mask = maxmask >> 6;
824 0 : while (mask > 4096)
825 0 : mask >>= 2;
826 : /* try out on first 25% of b */
827 0 : cnt1 = ci->ncand >> 2;
828 : }
829 :
830 13207 : o = canditer_next(ci); /* always one ahead */
831 0 : for (;;) {
832 13207 : lng t1 = 0;
833 13207 : TRC_DEBUG_IF(ACCELERATOR) t1 = GDKusec();
834 13207 : BUN maxslots = (mask >> 3) - 1; /* 1/8 full is too full */
835 :
836 13207 : h->nheads = 0;
837 13207 : h->nunique = 0;
838 13207 : p = 0;
839 13207 : HEAPfree(&h->heapbckt, true);
840 : /* create the hash structures */
841 26414 : if (HASHnew(h, ATOMtype(bi.type), BATcapacity(b),
842 : mask, ci->ncand, true) != GDK_SUCCEED) {
843 0 : HEAPfree(&h->heaplink, true);
844 0 : GDKfree(h);
845 0 : bat_iterator_end(&bi);
846 0 : return NULL;
847 : }
848 :
849 13207 : switch (tpe) {
850 10 : case TYPE_bte:
851 20 : starthash(bte);
852 : break;
853 56 : case TYPE_sht:
854 112 : starthash(sht);
855 : break;
856 12 : case TYPE_flt:
857 24 : starthash(flt);
858 : break;
859 11636 : case TYPE_int:
860 23272 : starthash(int);
861 : break;
862 7 : case TYPE_dbl:
863 14 : starthash(dbl);
864 : break;
865 746 : case TYPE_lng:
866 1492 : starthash(lng);
867 : break;
868 : #ifdef HAVE_HGE
869 7 : case TYPE_hge:
870 14 : starthash(hge);
871 : break;
872 : #endif
873 1 : case TYPE_uuid:
874 2 : starthash(uuid);
875 : break;
876 732 : default: {
877 732 : int (*atomcmp)(const void *, const void *) = ATOMcompare(h->type);
878 1464 : TIMEOUT_LOOP(p, qry_ctx) {
879 0 : const void *restrict v = BUNtail(bi, o - b->hseqbase);
880 0 : c = hash_any(h, v);
881 0 : hget = HASHget(h, c);
882 0 : if (hget == BUN_NONE) {
883 0 : if (h->nheads == maxslots)
884 0 : TIMEOUT_LOOP_BREAK; /* mask too full */
885 0 : h->nheads++;
886 0 : h->nunique++;
887 : } else {
888 : for (hb = hget;
889 0 : hb != BUN_NONE;
890 0 : hb = HASHgetlink(h, hb)) {
891 0 : if (atomcmp(v,
892 0 : BUNtail(bi, hb)) == 0)
893 : break;
894 : }
895 0 : h->nunique += hb == BUN_NONE;
896 : }
897 0 : HASHputlink(h, p, hget);
898 0 : HASHput(h, c, p);
899 0 : o = canditer_next(ci);
900 : }
901 732 : TIMEOUT_CHECK(qry_ctx,
902 : GOTO_LABEL_TIMEOUT_HANDLER(bailout, qry_ctx));
903 : break;
904 : }
905 : }
906 13207 : TRC_DEBUG_IF(ACCELERATOR) if (p < cnt1)
907 0 : TRC_DEBUG_ENDIF(ACCELERATOR,
908 : "%s: abort starthash with "
909 : "mask " BUNFMT " at " BUNFMT " after " LLFMT " usec\n", BATgetId(b), mask, p, GDKusec() - t1);
910 13207 : if (p == cnt1 || mask == maxmask)
911 : break;
912 0 : mask <<= 2;
913 : /* if we fill up the slots fast (p <= maxslots * 1.2)
914 : * increase mask size a bit more quickly */
915 0 : if (p == h->nunique) {
916 : /* only unique values so far: grow really fast */
917 : mask = maxmask;
918 : cnt1 = 0;
919 0 : } else if (mask < maxmask && p <= maxslots * 1.2)
920 0 : mask <<= 2;
921 0 : canditer_reset(ci);
922 0 : o = canditer_next(ci);
923 : }
924 :
925 : /* finish the hashtable with the current mask */
926 13207 : switch (tpe) {
927 10 : case TYPE_bte:
928 343 : finishhash(bte);
929 : break;
930 56 : case TYPE_sht:
931 577 : finishhash(sht);
932 : break;
933 11636 : case TYPE_int:
934 41339926 : finishhash(int);
935 : break;
936 12 : case TYPE_flt:
937 369 : finishhash(flt);
938 : break;
939 7 : case TYPE_dbl:
940 58 : finishhash(dbl);
941 : break;
942 746 : case TYPE_lng:
943 16625857 : finishhash(lng);
944 : break;
945 : #ifdef HAVE_HGE
946 7 : case TYPE_hge:
947 114 : finishhash(hge);
948 : break;
949 : #endif
950 1 : case TYPE_uuid:
951 2 : finishhash(uuid);
952 : break;
953 732 : default: {
954 732 : int (*atomcmp)(const void *, const void *) = ATOMcompare(h->type);
955 323440 : TIMEOUT_LOOP(ci->ncand - p, qry_ctx) {
956 321970 : const void *restrict v = BUNtail(bi, o - b->hseqbase);
957 321930 : c = hash_any(h, v);
958 321981 : hget = HASHget(h, c);
959 321981 : h->nheads += hget == BUN_NONE;
960 321981 : if (!hascand) {
961 : for (hb = hget;
962 423591 : hb != BUN_NONE;
963 101565 : hb = HASHgetlink(h, hb)) {
964 267975 : if (atomcmp(v, BUNtail(bi, hb)) == 0)
965 : break;
966 : }
967 321951 : h->nunique += hb == BUN_NONE;
968 : }
969 321906 : HASHputlink(h, p, hget);
970 321974 : HASHput(h, c, p);
971 321950 : o = canditer_next(ci);
972 321970 : p++;
973 : }
974 732 : TIMEOUT_CHECK(qry_ctx,
975 : GOTO_LABEL_TIMEOUT_HANDLER(bailout, qry_ctx));
976 : break;
977 : }
978 : }
979 13207 : bat_iterator_end(&bi);
980 : /* if the number of unique values is equal to the bat count,
981 : * all values are necessarily distinct */
982 13207 : MT_lock_set(&b->theaplock);
983 13207 : if (h->nunique == BATcount(b) && !b->tkey) {
984 9571 : b->tkey = true;
985 : }
986 13207 : if (ci->ncand == BATcount(b))
987 13036 : b->tunique_est = (double) h->nunique;
988 13207 : MT_lock_unset(&b->theaplock);
989 13207 : TRC_DEBUG_IF(ACCELERATOR) {
990 0 : TRC_DEBUG_ENDIF(ACCELERATOR,
991 : "hash construction " LLFMT " usec\n", GDKusec() - t0);
992 0 : HASHcollisions(b, h, __func__);
993 : }
994 : return h;
995 :
996 0 : bailout:
997 0 : bat_iterator_end(&bi);
998 0 : HEAPfree(&h->heaplink, true);
999 0 : HEAPfree(&h->heapbckt, true);
1000 0 : GDKfree(h);
1001 0 : return NULL;
1002 : }
1003 :
1004 : gdk_return
1005 2679162 : BAThash(BAT *b)
1006 : {
1007 2679162 : if (b->ttype == TYPE_void) {
1008 0 : GDKerror("No hash on void type bats\n");
1009 0 : return GDK_FAIL;
1010 : }
1011 2679162 : if (ATOMstorage(b->ttype) == TYPE_msk) {
1012 0 : GDKerror("No hash on msk type bats\n");
1013 0 : return GDK_FAIL;
1014 : }
1015 2679162 : if (BATcheckhash(b)) {
1016 : return GDK_SUCCEED;
1017 : }
1018 : #ifdef __COVERITY__
1019 : MT_rwlock_wrlock(&b->thashlock);
1020 : #else
1021 14071 : for (;;) {
1022 : /* If multiple threads simultaneously try to build a
1023 : * hash on a bat, e.g. in order to perform a join, it
1024 : * may happen that one thread succeeds in obtaining the
1025 : * write lock, then builds the hash, releases the lock,
1026 : * acquires the read lock, and performs the join. The
1027 : * other threads may then still be attempting to acquire
1028 : * the write lock. But now they have to wait until the
1029 : * read lock is released, which can be quite a long
1030 : * time. Especially if a second thread goes through the
1031 : * same process as the first. */
1032 14071 : if (MT_rwlock_wrtry(&b->thashlock))
1033 : break;
1034 1035 : MT_sleep_ms(1);
1035 1035 : if (MT_rwlock_rdtry(&b->thashlock)) {
1036 1035 : if (b->thash != NULL && b->thash != (Hash *) 1) {
1037 4 : MT_rwlock_rdunlock(&b->thashlock);
1038 4 : return GDK_SUCCEED;
1039 : }
1040 1031 : MT_rwlock_rdunlock(&b->thashlock);
1041 : }
1042 : }
1043 : #endif
1044 : /* we have the write lock */
1045 13035 : if (b->thash == NULL) {
1046 13035 : struct canditer ci;
1047 13035 : canditer_init(&ci, b, NULL);
1048 13036 : if ((b->thash = BAThash_impl(b, &ci, "thash")) == NULL) {
1049 0 : MT_rwlock_wrunlock(&b->thashlock);
1050 0 : return GDK_FAIL;
1051 : }
1052 : }
1053 13036 : MT_rwlock_wrunlock(&b->thashlock);
1054 13036 : return GDK_SUCCEED;
1055 : }
1056 :
1057 : /*
1058 : * The entry on which a value hashes can be calculated with the
1059 : * routine HASHprobe.
1060 : */
1061 : BUN
1062 450325157 : HASHprobe(const Hash *h, const void *v)
1063 : {
1064 865967542 : switch (ATOMbasetype(h->type)) {
1065 4023 : case TYPE_bte:
1066 4023 : return hash_bte(h, v);
1067 139249 : case TYPE_sht:
1068 139249 : return hash_sht(h, v);
1069 397399834 : case TYPE_int:
1070 397399834 : return hash_int(h, v);
1071 34858204 : case TYPE_lng:
1072 34858204 : return hash_lng(h, v);
1073 : #ifdef HAVE_HGE
1074 31593 : case TYPE_hge:
1075 31593 : return hash_hge(h, v);
1076 : #endif
1077 150932 : case TYPE_flt:
1078 150932 : return hash_flt(h, v);
1079 41051 : case TYPE_dbl:
1080 41051 : return hash_dbl(h, v);
1081 2104 : case TYPE_uuid:
1082 2104 : return hash_uuid(h, v);
1083 17698167 : default:
1084 17698167 : return hash_any(h, v);
1085 : }
1086 : }
1087 :
1088 : void
1089 490505 : HASHappend_locked(BAT *b, BUN i, const void *v)
1090 : {
1091 490505 : Hash *h = b->thash;
1092 490505 : if (h == NULL) {
1093 0 : return;
1094 : }
1095 490505 : if (h == (Hash *) 1) {
1096 0 : b->thash = NULL;
1097 0 : doHASHdestroy(b, h);
1098 0 : GDKclrerr();
1099 0 : return;
1100 : }
1101 490505 : assert(i * h->width == h->heaplink.free);
1102 490505 : if (h->nunique < b->batCount / hash_destroy_uniques_fraction) {
1103 0 : b->thash = NULL;
1104 0 : doHASHdestroy(b, h);
1105 0 : GDKclrerr();
1106 0 : return;
1107 : }
1108 490505 : if (HASHfix(h, false, true) != GDK_SUCCEED) {
1109 0 : b->thash = NULL;
1110 0 : doHASHdestroy(b, h);
1111 0 : GDKclrerr();
1112 0 : return;
1113 : }
1114 490505 : if (HASHwidth(i + 1) > h->width &&
1115 0 : HASHupgradehashheap(b) != GDK_SUCCEED) {
1116 0 : GDKclrerr();
1117 0 : return;
1118 : }
1119 981007 : if ((ATOMsize(b->ttype) > 2 &&
1120 490502 : HASHgrowbucket(b) != GDK_SUCCEED) ||
1121 491651 : ((i + 1) * h->width > h->heaplink.size &&
1122 1146 : HEAPextend(&h->heaplink,
1123 1146 : i * h->width + GDK_mmap_pagesize,
1124 : true) != GDK_SUCCEED)) {
1125 0 : b->thash = NULL;
1126 0 : HEAPfree(&h->heapbckt, true);
1127 0 : HEAPfree(&h->heaplink, true);
1128 0 : GDKfree(h);
1129 0 : GDKclrerr();
1130 0 : return;
1131 : }
1132 490505 : h->Link = h->heaplink.base;
1133 490505 : BUN c = HASHprobe(h, v);
1134 490505 : h->heaplink.free += h->width;
1135 490505 : BUN hb = HASHget(h, c);
1136 490505 : BUN hb2;
1137 490505 : BATiter bi = bat_iterator_nolock(b);
1138 490505 : int (*atomcmp)(const void *, const void *) = ATOMcompare(h->type);
1139 490505 : for (hb2 = hb;
1140 688705 : hb2 != BUN_NONE;
1141 198200 : hb2 = HASHgetlink(h, hb2)) {
1142 360680 : if (atomcmp(v, BUNtail(bi, hb2)) == 0)
1143 : break;
1144 : }
1145 490505 : h->nheads += hb == BUN_NONE;
1146 490505 : h->nunique += hb2 == BUN_NONE;
1147 490505 : HASHputlink(h, i, hb);
1148 490505 : HASHput(h, c, i);
1149 490505 : h->heapbckt.dirty = true;
1150 490505 : h->heaplink.dirty = true;
1151 : }
1152 :
1153 : BUN
1154 0 : HASHappend(BAT *b, BUN i, const void *v)
1155 : {
1156 0 : BUN nunique;
1157 0 : MT_rwlock_wrlock(&b->thashlock);
1158 0 : HASHappend_locked(b, i, v);
1159 0 : nunique = b->thash ? b->thash->nunique : 0;
1160 0 : MT_rwlock_wrunlock(&b->thashlock);
1161 0 : return nunique;
1162 : }
1163 :
1164 : /* insert value v at position p into the hash table of b */
1165 : void
1166 158726849 : HASHinsert_locked(BATiter *bi, BUN p, const void *v)
1167 : {
1168 158726849 : BAT *b = bi->b;
1169 158726849 : Hash *h = b->thash;
1170 158726849 : if (h == NULL) {
1171 : return;
1172 : }
1173 19389 : if (h == (Hash *) 1) {
1174 0 : b->thash = NULL;
1175 0 : doHASHdestroy(b, h);
1176 0 : GDKclrerr();
1177 0 : return;
1178 : }
1179 19389 : assert(p * h->width < h->heaplink.free);
1180 19389 : if (h->nunique < b->batCount / hash_destroy_uniques_fraction) {
1181 0 : b->thash = NULL;
1182 0 : doHASHdestroy(b, h);
1183 0 : GDKclrerr();
1184 0 : return;
1185 : }
1186 19389 : if (HASHfix(h, false, true) != GDK_SUCCEED) {
1187 0 : b->thash = NULL;
1188 0 : doHASHdestroy(b, h);
1189 0 : GDKclrerr();
1190 0 : return;
1191 : }
1192 19389 : BUN c = HASHprobe(h, v);
1193 19389 : BUN hb = HASHget(h, c);
1194 19389 : int (*atomcmp)(const void *, const void *) = ATOMcompare(h->type);
1195 19389 : if (hb == BUN_NONE || hb < p) {
1196 : /* bucket is empty, or bucket is used by lower numbered
1197 : * position */
1198 4421 : h->heaplink.dirty = true;
1199 4421 : h->heapbckt.dirty = true;
1200 4421 : HASHputlink(h, p, hb);
1201 4421 : HASHput(h, c, p);
1202 4421 : if (hb == BUN_NONE) {
1203 2096 : h->nheads++;
1204 : } else {
1205 2724 : do {
1206 2724 : if (atomcmp(v, BUNtail(*bi, hb)) == 0) {
1207 : /* found another row with the
1208 : * same value, so don't
1209 : * increment nunique */
1210 : return;
1211 : }
1212 1136 : hb = HASHgetlink(h, hb);
1213 1136 : } while (hb != BUN_NONE);
1214 : }
1215 : /* this is a new value */
1216 2833 : h->nunique++;
1217 2833 : return;
1218 : }
1219 : bool seen = false;
1220 132610 : for (;;) {
1221 132610 : if (!seen)
1222 15382 : seen = atomcmp(v, BUNtail(*bi, hb)) == 0;
1223 132610 : BUN hb2 = HASHgetlink(h, hb);
1224 132610 : if (hb2 == BUN_NONE || hb2 < p) {
1225 14968 : h->heaplink.dirty = true;
1226 14968 : HASHputlink(h, p, hb2);
1227 14968 : HASHputlink(h, hb, p);
1228 15257 : while (!seen && hb2 != BUN_NONE) {
1229 289 : seen = atomcmp(v, BUNtail(*bi, hb2)) == 0;
1230 289 : hb2 = HASHgetlink(h, hb2);
1231 : }
1232 14968 : if (!seen)
1233 306 : h->nunique++;
1234 14968 : return;
1235 : }
1236 : hb = hb2;
1237 : }
1238 : }
1239 :
1240 : BUN
1241 2 : HASHinsert(BATiter *bi, BUN p, const void *v)
1242 : {
1243 2 : BUN nunique;
1244 2 : MT_rwlock_wrlock(&bi->b->thashlock);
1245 2 : HASHinsert_locked(bi, p, v);
1246 2 : nunique = bi->b->thash ? bi->b->thash->nunique : 0;
1247 2 : MT_rwlock_wrunlock(&bi->b->thashlock);
1248 2 : return nunique;
1249 : }
1250 :
1251 : /* delete value v at position p from the hash table of b */
1252 : void
1253 158728901 : HASHdelete_locked(BATiter *bi, BUN p, const void *v)
1254 : {
1255 158728901 : BAT *b = bi->b;
1256 158728901 : Hash *h = b->thash;
1257 158728901 : if (h == NULL) {
1258 : return;
1259 : }
1260 19389 : if (h == (Hash *) 1) {
1261 0 : b->thash = NULL;
1262 0 : doHASHdestroy(b, h);
1263 0 : GDKclrerr();
1264 0 : return;
1265 : }
1266 19389 : assert(p * h->width < h->heaplink.free);
1267 19389 : if (h->nunique < b->batCount / hash_destroy_uniques_fraction) {
1268 0 : b->thash = NULL;
1269 0 : doHASHdestroy(b, h);
1270 0 : GDKclrerr();
1271 0 : return;
1272 : }
1273 19389 : if (HASHfix(h, false, true) != GDK_SUCCEED) {
1274 0 : b->thash = NULL;
1275 0 : doHASHdestroy(b, h);
1276 0 : GDKclrerr();
1277 0 : return;
1278 : }
1279 19389 : BUN c = HASHprobe(h, v);
1280 19389 : BUN hb = HASHget(h, c);
1281 19389 : int (*atomcmp)(const void *, const void *) = ATOMcompare(h->type);
1282 19389 : if (hb == p) {
1283 3221 : BUN hb2 = HASHgetlink(h, p);
1284 3221 : h->heaplink.dirty = true;
1285 3221 : h->heapbckt.dirty = true;
1286 3221 : HASHput(h, c, hb2);
1287 3221 : HASHputlink(h, p, BUN_NONE);
1288 3221 : if (hb2 == BUN_NONE) {
1289 1472 : h->nheads--;
1290 : } else {
1291 2191 : do {
1292 2191 : if (atomcmp(v, BUNtail(*bi, hb2)) == 0) {
1293 : /* found another row with the
1294 : * same value, so don't
1295 : * decrement nunique below */
1296 : return;
1297 : }
1298 1209 : hb2 = HASHgetlink(h, hb2);
1299 1209 : } while (hb2 != BUN_NONE);
1300 : }
1301 : /* no rows found with the same value, so number of
1302 : * unique values is one lower */
1303 2239 : h->nunique--;
1304 2239 : return;
1305 : }
1306 : bool seen = false;
1307 : BUN links = 0;
1308 127843 : for (;;) {
1309 127843 : if (!seen)
1310 16348 : seen = atomcmp(v, BUNtail(*bi, hb)) == 0;
1311 127843 : BUN hb2 = HASHgetlink(h, hb);
1312 127843 : assert(hb2 != BUN_NONE );
1313 127843 : assert(hb2 < hb);
1314 127843 : if (hb2 == p) {
1315 16168 : for (hb2 = HASHgetlink(h, hb2);
1316 16220 : !seen && hb2 != BUN_NONE;
1317 52 : hb2 = HASHgetlink(h, hb2))
1318 52 : seen = atomcmp(v, BUNtail(*bi, hb2)) == 0;
1319 16168 : break;
1320 : }
1321 111675 : hb = hb2;
1322 111675 : if (++links > hash_destroy_chain_length) {
1323 0 : b->thash = NULL;
1324 0 : doHASHdestroy(b, h);
1325 0 : GDKclrerr();
1326 0 : return;
1327 : }
1328 : }
1329 16168 : h->heaplink.dirty = true;
1330 16168 : HASHputlink(h, hb, HASHgetlink(h, p));
1331 16168 : HASHputlink(h, p, BUN_NONE);
1332 16168 : if (!seen)
1333 94 : h->nunique--;
1334 : }
1335 :
1336 : BUN
1337 6 : HASHdelete(BATiter *bi, BUN p, const void *v)
1338 : {
1339 6 : BUN nunique;
1340 6 : MT_rwlock_wrlock(&bi->b->thashlock);
1341 6 : HASHdelete_locked(bi, p, v);
1342 6 : nunique = bi->b->thash ? bi->b->thash->nunique : 0;
1343 6 : MT_rwlock_wrunlock(&bi->b->thashlock);
1344 6 : return nunique;
1345 : }
1346 :
1347 : BUN
1348 0 : HASHlist(Hash *h, BUN i)
1349 : {
1350 0 : BUN c = 1;
1351 0 : BUN j = HASHget(h, i);
1352 :
1353 0 : if (j == BUN_NONE)
1354 : return 1;
1355 0 : while ((j = HASHgetlink(h, i)) != BUN_NONE) {
1356 0 : c++;
1357 0 : i = j;
1358 : }
1359 : return c;
1360 : }
1361 :
1362 : void
1363 19170296 : HASHdestroy(BAT *b)
1364 : {
1365 19170296 : if (b) {
1366 19170296 : Hash *hs;
1367 19170296 : MT_rwlock_wrlock(&b->thashlock);
1368 19170956 : hs = b->thash;
1369 19170956 : b->thash = NULL;
1370 19170956 : MT_rwlock_wrunlock(&b->thashlock);
1371 19168638 : doHASHdestroy(b, hs);
1372 : }
1373 19169173 : }
1374 :
1375 : void
1376 355272 : HASHfree(BAT *b)
1377 : {
1378 355272 : if (b) {
1379 355272 : Hash *h;
1380 355272 : MT_rwlock_wrlock(&b->thashlock);
1381 355272 : if ((h = b->thash) != NULL && h != (Hash *) 1) {
1382 1999 : bool rmheap = h->heaplink.dirty || h->heapbckt.dirty;
1383 1999 : TRC_DEBUG(ACCELERATOR, ALGOBATFMT " free hash %s\n",
1384 : ALGOBATPAR(b),
1385 : rmheap ? "removing" : "keeping");
1386 :
1387 1999 : b->thash = rmheap ? NULL : (Hash *) 1;
1388 1999 : HEAPfree(&h->heapbckt, rmheap);
1389 1999 : HEAPfree(&h->heaplink, rmheap);
1390 1999 : GDKfree(h);
1391 : }
1392 355272 : MT_rwlock_wrunlock(&b->thashlock);
1393 : }
1394 355272 : }
|