You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dynamically grow the hash table with bound unknown
In forder() and rbindlist(), there is no good upper boundary on the
number of elements in the hash known ahead of time. Grow the hash table
dynamically. Since the R/W locks are far too slow and OpenMP atomics are
too limited, rely on strategically placed flushes, which isn't really a
solution.
// group numbers are left in truelength to be fetched by WRITE_KEY
292
292
{
293
293
intna_count=0;
@@ -302,13 +302,13 @@ static void range_str(const SEXP *x, int n, uint64_t *out_min, uint64_t *out_max
302
302
na_count++;
303
303
continue;
304
304
}
305
-
// Why is it acceptable to call hash_lookup when marks can be shared between threads?
306
-
// 1. There are no pointers to follow for hash_lookup() inside the hash table, so there's no danger of crashing by following a partially written pointer.
307
-
// 2. If another thread writes s into the hash but hash_lookup() fails to see a non-zero value, we'll safely check it again in the critical section below.
305
+
// Why is it acceptable to call dhash_lookup when marks can be shared between threads?
306
+
// 1. We have rwlocks to avoid crashing on a pointer being invalidated by a different thread.
307
+
// 2. We check again after entering the critical section.
308
308
// 3. We only change the marks from zero to nonzero, so once a nonzero value is seen, it must be correct.
309
-
if (hash_lookup(marks,s,0)<0) continue; // seen this group before
310
-
#pragma omp critical
311
-
if (hash_lookup(marks,s,0)>=0) { // another thread may have set it while I was waiting, so check it again
309
+
if (dhash_lookup(marks,s,0)<0) continue; // seen this group before
310
+
#pragma omp critical(range_str_write)
311
+
if (dhash_lookup(marks,s,0)>=0) { // another thread may have set it while I was waiting, so check it again
312
312
// now save unique SEXP in ustr so we can loop sort uniques when sorting too
313
313
if (ustr_alloc<=ustr_n) {
314
314
ustr_alloc= (ustr_alloc==0) ? 16384 : ustr_alloc*2; // small initial guess, negligible time to alloc 128KB (32 pages)
@@ -317,7 +317,7 @@ static void range_str(const SEXP *x, int n, uint64_t *out_min, uint64_t *out_max
317
317
if (ustr==NULL) STOP(_("Unable to realloc %d * %d bytes in range_str"), ustr_alloc, (int)sizeof(SEXP)); // # nocov
318
318
}
319
319
ustr[ustr_n++] =s;
320
-
hash_set(marks, s, -ustr_n); // unique in any order is fine. first-appearance order is achieved later in count_group
320
+
dhash_set(marks, s, -ustr_n); // unique in any order is fine. first-appearance order is achieved later in count_group
321
321
if (LENGTH(s)>ustr_maxlen) ustr_maxlen=LENGTH(s);
322
322
if (!anynotutf8&&// even if anynotascii we still want to know if anynotutf8, and anynotutf8 implies anynotascii already
323
323
!IS_ASCII(s)) { // anynotutf8 implies anynotascii and IS_ASCII will be cheaper than IS_UTF8, so start with this one
@@ -350,20 +350,20 @@ static void range_str(const SEXP *x, int n, uint64_t *out_min, uint64_t *out_max
350
350
if (LENGTH(s)>ustr_maxlen) ustr_maxlen=LENGTH(s);
351
351
}
352
352
cradix(ustr3, ustr_n); // sort to detect possible duplicates after converting; e.g. two different non-utf8 map to the same utf8
353
-
hash_set(marks, ustr3[0], -1);
353
+
dhash_set(marks, ustr3[0], -1);
354
354
into=-1;
355
355
for (inti=1; i<ustr_n; i++) {
356
356
if (ustr3[i] ==ustr3[i-1]) continue; // use the same o for duplicates
357
-
hash_set(marks, ustr3[i], --o);
357
+
dhash_set(marks, ustr3[i], --o);
358
358
}
359
359
// now use the 1-1 mapping from ustr to ustr2 to get the ordering back into original ustr, being careful to reset tl to 0
360
360
int*tl= (int*)malloc(ustr_n*sizeof(int));
361
361
if (!tl)
362
362
STOP(_("Failed to alloc tl when converting strings to UTF8")); // # nocov
363
363
constSEXP*tt=STRING_PTR_RO(ustr2);
364
-
for (inti=0; i<ustr_n; i++) tl[i] =hash_lookup(marks, tt[i], 0); // fetches the o in ustr3 into tl which is ordered by ustr
365
-
for (inti=0; i<ustr_n; i++) hash_set(marks, ustr3[i], 0); // reset to 0 tl of the UTF8 (and possibly non-UTF in ustr too)
366
-
for (inti=0; i<ustr_n; i++) hash_set(marks, ustr[i], tl[i]); // put back the o into ustr's tl
364
+
for (inti=0; i<ustr_n; i++) tl[i] =dhash_lookup(marks, tt[i], 0); // fetches the o in ustr3 into tl which is ordered by ustr
365
+
for (inti=0; i<ustr_n; i++) dhash_set(marks, ustr3[i], 0); // reset to 0 tl of the UTF8 (and possibly non-UTF in ustr too)
366
+
for (inti=0; i<ustr_n; i++) dhash_set(marks, ustr[i], tl[i]); // put back the o into ustr's tl
367
367
free(tl);
368
368
free(ustr3);
369
369
UNPROTECT(1);
@@ -376,7 +376,7 @@ static void range_str(const SEXP *x, int n, uint64_t *out_min, uint64_t *out_max
376
376
// that this is always ascending; descending is done in WRITE_KEY using max-this
377
377
cradix(ustr, ustr_n); // sorts ustr in-place by reference. assumes NA_STRING not present.
378
378
for(inti=0; i<ustr_n; i++) // save ordering in the CHARSXP. negative so as to distinguish with R's own usage.
379
-
hash_set(marks, ustr[i], -i-1);
379
+
dhash_set(marks, ustr[i], -i-1);
380
380
}
381
381
// else group appearance order was already saved to tl in the first pass
// The 4 lowest bits of the pointer are probably zeroes because a typical SEXPREC exceeds 16 bytes in size.
50
-
// Since SEXPRECs are heap-allocated, they are subject to malloc() alignment guarantees, which is at least 4 bytes on 32-bit platforms, most likely more than 8 bytes.
59
+
// Since SEXPRECs are heap-allocated, they are subject to malloc() alignment guarantees,
60
+
// which is at least 4 bytes on 32-bit platforms, most likely more than 8 bytes.
0 commit comments