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