LCOV - code coverage report
Current view: top level - gdk - gdk_imprints.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 207 416 49.8 %
Date: 2024-10-03 20:03:20 Functions: 8 11 72.7 %

          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             :  * Implementation for the column imprints index.
      15             :  * See paper:
      16             :  * Column Imprints: A Secondary Index Structure,
      17             :  * L.Sidirourgos and M.Kersten.
      18             :  */
      19             : 
      20             : #include "monetdb_config.h"
      21             : #include "gdk.h"
      22             : #include "gdk_private.h"
      23             : #include "gdk_imprints.h"
      24             : 
      25             : /*
      26             :  * The imprints heap consists of five parts:
      27             :  * - header
      28             :  * - bins
      29             :  * - stats
      30             :  * - imps
      31             :  * - dict
      32             :  *
      33             :  * The header consists of four size_t values `size_t hdata[4]':
      34             :  * - hdata[0] = (1 << 16) | (IMPRINTS_VERSION << 8) | (uint8_t) imprints->bits
      35             :  * - hdata[1] = imprints->impcnt
      36             :  * - hdata[2] = imprints->dictcnt
      37             :  * - hdata[3] = BATcount(b)
      38             :  * The first word of the header includes a version number in case the
      39             :  * format changes, and a bit to indicate that the data was synced to disk.
      40             :  * This bit is the last thing written, so if it is set when reading back
      41             :  * the imprints heap, the data is complete.  The fourth word is the size of
      42             :  * the BAT for which the imprints were created.  If this size differs from
      43             :  * the size of the BAT at the time of reading the imprints back from disk,
      44             :  * the imprints are not usable.
      45             :  *
      46             :  * The bins area starts immediately after the header.  It consists of 64
      47             :  * values in the domain of the BAT `TYPE bins[64]'.
      48             :  *
      49             :  * The stats area starts immediately after the bins area.  It consists of
      50             :  * three times an array of 64 64 (32) bit integers `BUN stats[3][64]'.  The
      51             :  * three arrays represent respectively min, max, and cnt for each of the 64
      52             :  * bins, so stats can be seen as `BUN min_bins[64]; BUN max_bins[64]; BUN
      53             :  * cnt_bins[64];'.  The min and max values are positions of the smallest
      54             :  * and largest non-nil value in the corresponding bin.
      55             :  *
      56             :  * The imps area starts immediately after the stats area.  It consists of
      57             :  * one mask per "page" of the input BAT indicating in which bins the values
      58             :  * in that page fall.  The size of the mask is given by imprints->bits.
      59             :  * The list of masks may be run-length compressed, see the dict area.  A
      60             :  * "page" is 64 bytes worth of values, so the number of values depends on
      61             :  * the type of the value.
      62             :  *
      63             :  * The dict area starts immediately after the imps area.  It consists of
      64             :  * one cchdc_t value per "page" of the input.  The precise use is described
      65             :  * below.
      66             :  *
      67             :  * There are up to 64 bins into which values are sorted.  The number of
      68             :  * bins depends on the number of unique values in the input BAT (actually
      69             :  * on the number of unique values in a random sample of 2048 values of the
      70             :  * input BAT) and is 8, 16, 32, or 64.  The number of bits in the mask is
      71             :  * stored in imprints->bits.  The boundaries of the bins are dynamically
      72             :  * determined when the imprints are created and stored in the bins array.
      73             :  * In fact, bins[n] contains the lower boundary of the n-th bin (0 <= n <
      74             :  * N, N the number of bins).  The value of bins[0] is not actually used:
      75             :  * all values smaller than bins[1] are sorted into this bin, including NIL.
      76             :  * The boundaries are simply computed by stepping with large steps through
      77             :  * the sorted sample and taking 63 (or 31, 15, 7) equally spaced values
      78             :  * from there.
      79             :  *
      80             :  * Once the appropriate bin n is determined for a particular value v
      81             :  * (bins[n] <= v < bins[n+1]), a bitmask can be constructed for the value
      82             :  * as ((uintN_t)1 << n) where N is the number of bits that are used for the
      83             :  * bitmasks and n is the number of the bin (0 <= n < N).
      84             :  *
      85             :  * The input BAT is divided into "pages" where a page is 64 bytes.  This
      86             :  * means the number of rows in a page depends on the size of the values: 64
      87             :  * for byte-sized values down to 4 for hugeint (128 bit) values.  For each
      88             :  * page, a bitmask is created which is the imprint for that page.  The
      89             :  * bitmask has a bit set for each bin into which a value inside the page
      90             :  * falls.  These bitmasks (imprints) are stored in the imprints->imps
      91             :  * array, but with a twist, see below.
      92             :  *
      93             :  * The imprints->dict array is an array of cchdc_t values.  A cchdc_t value
      94             :  * consists of a bit .repeat and a 24-bit value .cnt.  The sum of the .cnt
      95             :  * values is equal to the total number of pages in the input BAT.  If the
      96             :  * .repeat value is 0, there are .cnt consecutive imprint bitmasks in the
      97             :  * imprints->imps array, each for one page.  If the .repeat value is 1,
      98             :  * there is a single imprint bitmask in the imprints->imps array which is
      99             :  * valid for the next .cnt pages.  In this way a run-length encoding
     100             :  * compression scheme is implemented for imprints.
     101             :  *
     102             :  * Imprints are used for range selects, i.e. finding all rows in a BAT
     103             :  * whose value is inside some given range, or alternatively, all rows in a
     104             :  * BAT whose value is outside some given range (anti select).
     105             :  *
     106             :  * A range necessarily covers one or more consecutive bins.  A bit mask is
     107             :  * created for all bins that fall fully inside the range being selected (in
     108             :  * gdk_select.c called "innermask"), and a bit mask is created for all bins
     109             :  * that fall fully or partially inside the range (called "mask" in
     110             :  * gdk_select.c).  Note that for an "anti" select, i.e. a select which
     111             :  * matches everything except a given range, the bits in the bit masks are
     112             :  * not consecutive.
     113             :  *
     114             :  * We then go through the imps table.  All pages where the only set bits
     115             :  * are also set in "innermask" can be blindly added to the result: all
     116             :  * values fall inside the range.  All pages where none of the set bits are
     117             :  * also set in "mask" can be blindly skipped: no value falls inside the
     118             :  * range.  For the remaining pages, we scan the page and check each
     119             :  * individual value to see whether it is selected.
     120             :  *
     121             :  * Extra speed up is achieved by the run-length encoding of the imps table.
     122             :  * If a mask is in the category of fully inside the range or fully outside,
     123             :  * the complete set of pages can be added/skipped in one go.
     124             :  */
     125             : 
     126             : #define IMPRINTS_VERSION        2
     127             : #define IMPRINTS_HEADER_SIZE    4 /* nr of size_t fields in header */
     128             : 
     129             : #define BINSIZE(B, FUNC, T)                             \
     130             :         do {                                            \
     131             :                 switch (B) {                            \
     132             :                 case 8: FUNC(T,8); break;               \
     133             :                 case 16: FUNC(T,16); break;             \
     134             :                 case 32: FUNC(T,32); break;             \
     135             :                 case 64: FUNC(T,64); break;             \
     136             :                 default: MT_UNREACHABLE(); break;       \
     137             :                 }                                       \
     138             :         } while (0)
     139             : 
     140             : 
     141             : #define GETBIN(Z,X,B)                           \
     142             :         do {                                    \
     143             :                 Z = 0;                          \
     144             :                 for (int _i = 1; _i < B; _i++)       \
     145             :                         Z += ((X) >= bins[_i]);      \
     146             :         } while (0)
     147             : 
     148             : 
     149             : #define IMPS_CREATE(TYPE,B)                                             \
     150             :         do {                                                            \
     151             :                 uint##B##_t mask, prvmask;                              \
     152             :                 uint##B##_t *restrict im = (uint##B##_t *) imps;        \
     153             :                 const TYPE *restrict col = (TYPE *) bi->base;                \
     154             :                 const TYPE *restrict bins = (TYPE *) inbins;            \
     155             :                 const BUN page = IMPS_PAGE / sizeof(TYPE);              \
     156             :                 prvmask = 0;                                            \
     157             :                 for (i = 0; i < bi->count; ) {                            \
     158             :                         const BUN lim = MIN(i + page, bi->count);    \
     159             :                         /* new mask */                                  \
     160             :                         mask = 0;                                       \
     161             :                         /* build mask for all BUNs in one PAGE */       \
     162             :                         for ( ; i < lim; i++) {                              \
     163             :                                 const TYPE val = col[i];                \
     164             :                                 GETBIN(bin,val,B);                      \
     165             :                                 mask = IMPSsetBit(B,mask,bin);          \
     166             :                                 if (!is_##TYPE##_nil(val)) { /* do not count nils */ \
     167             :                                         if (!cnt_bins[bin]++) {         \
     168             :                                                 /* first in the bin */  \
     169             :                                                 min_bins[bin] = max_bins[bin] = i; \
     170             :                                         } else {                        \
     171             :                                                 if (val < col[min_bins[bin]]) \
     172             :                                                         min_bins[bin] = i; \
     173             :                                                 if (val > col[max_bins[bin]]) \
     174             :                                                         max_bins[bin] = i; \
     175             :                                         }                               \
     176             :                                 }                                       \
     177             :                         }                                               \
     178             :                         /* same mask as previous and enough count to add */ \
     179             :                         if ((prvmask == mask) && (dcnt > 0) &&               \
     180             :                             (dict[dcnt-1].cnt < (IMPS_MAX_CNT-1))) { \
     181             :                                 /* not a repeat header */               \
     182             :                                 if (!dict[dcnt-1].repeat) {             \
     183             :                                         /* if compressed */             \
     184             :                                         if (dict[dcnt-1].cnt > 1) {  \
     185             :                                                 /* uncompress last */   \
     186             :                                                 dict[dcnt-1].cnt--;     \
     187             :                                                 /* new header */        \
     188             :                                                 dict[dcnt].cnt = 1;     \
     189             :                                                 dict[dcnt].flags = 0;   \
     190             :                                                 dcnt++;                 \
     191             :                                         }                               \
     192             :                                         /* set repeat */                \
     193             :                                         dict[dcnt-1].repeat = 1;        \
     194             :                                 }                                       \
     195             :                                 /* increase cnt */                      \
     196             :                                 dict[dcnt-1].cnt++;                     \
     197             :                         } else { /* new mask (or run out of header count) */ \
     198             :                                 prvmask=mask;                           \
     199             :                                 im[icnt] = mask;                        \
     200             :                                 icnt++;                                 \
     201             :                                 if ((dcnt > 0) && !(dict[dcnt-1].repeat) && \
     202             :                                     (dict[dcnt-1].cnt < (IMPS_MAX_CNT-1))) { \
     203             :                                         dict[dcnt-1].cnt++;             \
     204             :                                 } else {                                \
     205             :                                         dict[dcnt].cnt = 1;             \
     206             :                                         dict[dcnt].repeat = 0;          \
     207             :                                         dict[dcnt].flags = 0;           \
     208             :                                         dcnt++;                         \
     209             :                                 }                                       \
     210             :                         }                                               \
     211             :                 }                                                       \
     212             :         } while (0)
     213             : 
     214             : static void
     215          52 : imprints_create(BAT *b, BATiter *bi, void *inbins, BUN *stats, bte bits,
     216             :                 void *imps, BUN *impcnt, cchdc_t *dict, BUN *dictcnt)
     217             : {
     218          52 :         BUN i;
     219          52 :         BUN dcnt, icnt;
     220          52 :         BUN *restrict min_bins = stats;
     221          52 :         BUN *restrict max_bins = min_bins + 64;
     222          52 :         BUN *restrict cnt_bins = max_bins + 64;
     223          52 :         int bin = 0;
     224          52 :         dcnt = icnt = 0;
     225             : #ifndef NDEBUG
     226          52 :         memset(min_bins, 0, 64 * SIZEOF_BUN);
     227          52 :         memset(max_bins, 0, 64 * SIZEOF_BUN);
     228             : #endif
     229          52 :         memset(cnt_bins, 0, 64 * SIZEOF_BUN);
     230             : 
     231          93 :         switch (ATOMbasetype(b->ttype)) {
     232           3 :         case TYPE_bte:
     233          69 :                 BINSIZE(bits, IMPS_CREATE, bte);
     234             :                 break;
     235           1 :         case TYPE_sht:
     236          34 :                 BINSIZE(bits, IMPS_CREATE, sht);
     237             :                 break;
     238          13 :         case TYPE_int:
     239         367 :                 BINSIZE(bits, IMPS_CREATE, int);
     240             :                 break;
     241          25 :         case TYPE_lng:
     242    64125979 :                 BINSIZE(bits, IMPS_CREATE, lng);
     243             :                 break;
     244             : #ifdef HAVE_HGE
     245           5 :         case TYPE_hge:
     246         750 :                 BINSIZE(bits, IMPS_CREATE, hge);
     247             :                 break;
     248             : #endif
     249           3 :         case TYPE_flt:
     250         102 :                 BINSIZE(bits, IMPS_CREATE, flt);
     251             :                 break;
     252           2 :         case TYPE_dbl:
     253          68 :                 BINSIZE(bits, IMPS_CREATE, dbl);
     254             :                 break;
     255             :         default:
     256             :                 /* should never reach here */
     257           0 :                 MT_UNREACHABLE();
     258             :         }
     259             : 
     260          52 :         *dictcnt = dcnt;
     261          52 :         *impcnt = icnt;
     262          52 : }
     263             : 
     264             : #ifdef NDEBUG
     265             : #define CLRMEM()        ((void) 0)
     266             : #else
     267             : #define CLRMEM()        while (k < 64) h[k++] = 0
     268             : #endif
     269             : 
     270             : #define FILL_HISTOGRAM(TYPE)                                            \
     271             :         do {                                                            \
     272             :                 BUN k;                                                  \
     273             :                 TYPE *restrict s = (TYPE *) Tloc(s4, 0);                \
     274             :                 TYPE *restrict h = imprints->bins;                   \
     275             :                 if (cnt < 64-1) {                                    \
     276             :                         TYPE max = GDK_##TYPE##_max;                    \
     277             :                         for (k = 0; k < cnt; k++)                    \
     278             :                                 h[k] = s[k];                            \
     279             :                         while (k < (BUN) imprints->bits)          \
     280             :                                 h[k++] = max;                           \
     281             :                         CLRMEM();                                       \
     282             :                 } else {                                                \
     283             :                         double y, ystep = (double) cnt / (64 - 1);      \
     284             :                         for (k = 0, y = 0; (BUN) y < cnt; y += ystep, k++) \
     285             :                                 h[k] = s[(BUN) y];                      \
     286             :                         if (k == 64 - 1) /* there is one left */        \
     287             :                                 h[k] = s[cnt - 1];                      \
     288             :                 }                                                       \
     289             :         } while (0)
     290             : 
     291             : /* Check whether we have imprints on b (and return true if we do).  It
     292             :  * may be that the imprints were made persistent, but we hadn't seen
     293             :  * that yet, so check the file system.  This also returns true if b is
     294             :  * a view and there are imprints on b's parent.
     295             :  *
     296             :  * Note that the b->timprints pointer can be NULL, meaning there are
     297             :  * no imprints; (Imprints *) 1, meaning there are no imprints loaded,
     298             :  * but they may exist on disk; or a valid pointer to loaded imprints.
     299             :  * These values are maintained here, in the IMPSdestroy and IMPSfree
     300             :  * functions, and in BBPdiskscan during initialization. */
     301             : bool
     302         176 : BATcheckimprints(BAT *b)
     303             : {
     304         176 :         bool ret;
     305         176 :         BATiter bi = bat_iterator(b);
     306             : 
     307         176 :         if (VIEWtparent(b)) {
     308          25 :                 assert(b->timprints == NULL);
     309          25 :                 b = BATdescriptor(VIEWtparent(b));
     310          25 :                 if (b == NULL) {
     311           0 :                         bat_iterator_end(&bi);
     312           0 :                         return false;
     313             :                 }
     314             :         }
     315             : 
     316         176 :         MT_lock_set(&b->batIdxLock);
     317         176 :         if (b->timprints == (Imprints *) 1) {
     318           0 :                 Imprints *imprints;
     319           0 :                 const char *nme = BBP_physical(b->batCacheid);
     320             : 
     321           0 :                 assert(!GDKinmemory(bi.h->farmid));
     322           0 :                 b->timprints = NULL;
     323           0 :                 if ((imprints = GDKzalloc(sizeof(Imprints))) != NULL &&
     324           0 :                     (imprints->imprints.farmid = BBPselectfarm(b->batRole, bi.type, imprintsheap)) >= 0) {
     325           0 :                         int fd;
     326             : 
     327           0 :                         strconcat_len(imprints->imprints.filename,
     328             :                                       sizeof(imprints->imprints.filename),
     329             :                                       nme, ".timprints", NULL);
     330           0 :                         imprints->imprints.storage = imprints->imprints.newstorage = STORE_INVALID;
     331             :                         /* check whether a persisted imprints index
     332             :                          * can be found */
     333           0 :                         if ((fd = GDKfdlocate(imprints->imprints.farmid, nme, "rb", "timprints")) >= 0) {
     334           0 :                                 size_t hdata[4];
     335           0 :                                 struct stat st;
     336           0 :                                 size_t pages;
     337             : 
     338           0 :                                 pages = (((size_t) bi.count * bi.width) + IMPS_PAGE - 1) / IMPS_PAGE;
     339           0 :                                 if (read(fd, hdata, sizeof(hdata)) == sizeof(hdata) &&
     340           0 :                                     hdata[0] & ((size_t) 1 << 16) &&
     341           0 :                                     ((hdata[0] & 0xFF00) >> 8) == IMPRINTS_VERSION &&
     342           0 :                                     hdata[3] == (size_t) bi.count &&
     343           0 :                                     fstat(fd, &st) == 0 &&
     344           0 :                                     st.st_size >= (off_t) (imprints->imprints.size =
     345           0 :                                                            imprints->imprints.free =
     346           0 :                                                            64 * bi.width +
     347             :                                                            64 * 3 * SIZEOF_BUN +
     348           0 :                                                            pages * ((bte) hdata[0] / 8) +
     349           0 :                                                            hdata[2] * sizeof(cchdc_t) +
     350             :                                                            sizeof(uint64_t) /* padding for alignment */
     351           0 :                                                            + 4 * SIZEOF_SIZE_T) &&
     352           0 :                                     HEAPload(&imprints->imprints, nme, "timprints", false) == GDK_SUCCEED) {
     353             :                                         /* usable */
     354           0 :                                         imprints->bits = (bte) (hdata[0] & 0xFF);
     355           0 :                                         imprints->impcnt = (BUN) hdata[1];
     356           0 :                                         imprints->dictcnt = (BUN) hdata[2];
     357           0 :                                         imprints->bins = imprints->imprints.base + 4 * SIZEOF_SIZE_T;
     358           0 :                                         imprints->stats = (BUN *) ((char *) imprints->bins + 64 * bi.width);
     359           0 :                                         imprints->imps = (void *) (imprints->stats + 64 * 3);
     360           0 :                                         imprints->dict = (void *) ((uintptr_t) ((char *) imprints->imps + pages * (imprints->bits / 8) + sizeof(uint64_t)) & ~(sizeof(uint64_t) - 1));
     361           0 :                                         close(fd);
     362           0 :                                         imprints->imprints.parentid = b->batCacheid;
     363           0 :                                         imprints->imprints.hasfile = true;
     364           0 :                                         ATOMIC_INIT(&imprints->imprints.refs, 1);
     365           0 :                                         b->timprints = imprints;
     366           0 :                                         TRC_DEBUG(ACCELERATOR, ALGOBATFMT " reusing persisted imprints\n", ALGOBATPAR(b));
     367           0 :                                         MT_lock_unset(&b->batIdxLock);
     368           0 :                                         if (bi.b != b)
     369           0 :                                                 BBPunfix(b->batCacheid);
     370           0 :                                         bat_iterator_end(&bi);
     371           0 :                                         return true;
     372             :                                 }
     373           0 :                                 close(fd);
     374             :                                 /* unlink unusable file */
     375           0 :                                 GDKunlink(imprints->imprints.farmid, BATDIR, nme, "timprints");
     376           0 :                                 imprints->imprints.hasfile = false;
     377             :                         }
     378             :                 }
     379           0 :                 GDKfree(imprints);
     380           0 :                 GDKclrerr();    /* we're not currently interested in errors */
     381             :         }
     382         176 :         MT_lock_unset(&b->batIdxLock);
     383         176 :         if (bi.b != b)
     384          25 :                 BBPunfix(b->batCacheid);
     385         176 :         bat_iterator_end(&bi);
     386         176 :         ret = b->timprints != NULL;
     387         176 :         if( ret)
     388           0 :                 TRC_DEBUG(ACCELERATOR, ALGOBATFMT " already has imprints\n", ALGOBATPAR(b));
     389             :         return ret;
     390             : }
     391             : 
     392             : static void
     393          41 : BATimpsync(void *arg)
     394             : {
     395          41 :         BAT *b = arg;
     396          41 :         Imprints *imprints;
     397          41 :         int fd;
     398          41 :         lng t0 = GDKusec();
     399          41 :         const char *failed = " failed";
     400             : 
     401          41 :         MT_lock_set(&b->batIdxLock);
     402          41 :         if ((imprints = b->timprints) != NULL) {
     403          41 :                 Heap *hp = &imprints->imprints;
     404          41 :                 if (HEAPsave(hp, hp->filename, NULL, true, hp->free, NULL) == GDK_SUCCEED) {
     405          41 :                         if (hp->storage == STORE_MEM) {
     406          41 :                                 if ((fd = GDKfdlocate(hp->farmid, hp->filename, "rb+", NULL)) >= 0) {
     407             :                                         /* add version number */
     408          41 :                                         ((size_t *) hp->base)[0] |= (size_t) IMPRINTS_VERSION << 8;
     409             :                                         /* sync-on-disk checked bit */
     410          41 :                                         ((size_t *) hp->base)[0] |= (size_t) 1 << 16;
     411          41 :                                         if (write(fd, hp->base, SIZEOF_SIZE_T) >= 0) {
     412          41 :                                                 failed = ""; /* not failed */
     413          41 :                                                 if (!(ATOMIC_GET(&GDKdebug) & NOSYNCMASK)) {
     414             : #if defined(NATIVE_WIN32)
     415             :                                                         _commit(fd);
     416             : #elif defined(HAVE_FDATASYNC)
     417           0 :                                                         fdatasync(fd);
     418             : #elif defined(HAVE_FSYNC)
     419             :                                                         fsync(fd);
     420             : #endif
     421             :                                                 }
     422          41 :                                                 hp->dirty = false;
     423             :                                         } else {
     424           0 :                                                 failed = " write failed";
     425           0 :                                                 perror("write hash");
     426             :                                         }
     427          41 :                                         close(fd);
     428             :                                 }
     429             :                         } else {
     430             :                                 /* add version number */
     431           0 :                                 ((size_t *) hp->base)[0] |= (size_t) IMPRINTS_VERSION << 8;
     432             :                                 /* sync-on-disk checked bit */
     433           0 :                                 ((size_t *) hp->base)[0] |= (size_t) 1 << 16;
     434           0 :                                 if (!(ATOMIC_GET(&GDKdebug) & NOSYNCMASK) &&
     435           0 :                                     MT_msync(hp->base, SIZEOF_SIZE_T) < 0) {
     436           0 :                                         failed = " sync failed";
     437           0 :                                         ((size_t *) hp->base)[0] &= ~((size_t) IMPRINTS_VERSION << 8);
     438             :                                 } else {
     439           0 :                                         hp->dirty = false;
     440           0 :                                         failed = ""; /* not failed */
     441             :                                 }
     442             :                         }
     443          41 :                         TRC_DEBUG(ACCELERATOR, ALGOBATFMT " imprints persisted "
     444             :                                   "(" LLFMT " usec)%s\n", ALGOBATPAR(b),
     445             :                                   GDKusec() - t0, failed);
     446             :                 }
     447             :         }
     448          41 :         MT_lock_unset(&b->batIdxLock);
     449          41 :         BBPunfix(b->batCacheid);
     450          41 : }
     451             : 
     452             : gdk_return
     453          61 : BATimprints(BAT *b)
     454             : {
     455          61 :         BAT *s1 = NULL, *s2 = NULL, *s3 = NULL, *s4 = NULL;
     456          61 :         bat unfix = 0;
     457          61 :         Imprints *imprints;
     458          61 :         BATiter bi;
     459          61 :         lng t0 = GDKusec();
     460             : 
     461          61 :         BATcheck(b, GDK_FAIL);
     462             : 
     463             :         /* we only create imprints for types that look like types we know */
     464          61 :         if (!imprintable(b->ttype)) {
     465             :                 /* doesn't look enough like base type: do nothing */
     466           9 :                 GDKerror("unsupported type\n");
     467           9 :                 return GDK_FAIL;
     468             :         }
     469             : 
     470          52 :         if (BATcheckimprints(b))
     471             :                 return GDK_SUCCEED;
     472             : 
     473          52 :         if (VIEWtparent(b)) {
     474             :                 /* views always keep null pointer and need to obtain
     475             :                  * the latest imprint from the parent at query time */
     476           0 :                 s2 = b;         /* remember for ACCELDEBUG print */
     477           0 :                 b = BATdescriptor(VIEWtparent(b));
     478           0 :                 if (b == NULL)
     479             :                         return GDK_FAIL;
     480           0 :                 unfix = b->batCacheid; /* bat to be unfixed */
     481           0 :                 if (BATcheckimprints(b)) {
     482           0 :                         BBPunfix(unfix);
     483           0 :                         return GDK_SUCCEED;
     484             :                 }
     485             :         }
     486          52 :         bi = bat_iterator(b);
     487          52 :         MT_lock_set(&b->batIdxLock);
     488             : 
     489          52 :         if (b->timprints == NULL) {
     490          52 :                 BUN cnt;
     491          52 :                 const char *nme = GDKinmemory(bi.h->farmid) ? ":memory:" : BBP_physical(b->batCacheid);
     492          52 :                 size_t pages;
     493             : 
     494          52 :                 MT_lock_unset(&b->batIdxLock);
     495          52 :                 MT_thread_setalgorithm("create imprints");
     496             : 
     497          52 :                 if (s2)
     498           0 :                         TRC_DEBUG(ACCELERATOR, ALGOBATFMT
     499             :                                   " creating imprints on parent "
     500             :                                   ALGOBATFMT "\n",
     501             :                                   ALGOBATPAR(s2), ALGOBATPAR(b));
     502             :                 else
     503          52 :                         TRC_DEBUG(ACCELERATOR, ALGOBATFMT
     504             :                                   " creating imprints\n",
     505             :                                   ALGOBATPAR(b));
     506             : 
     507          52 :                 s2 = NULL;
     508             : 
     509          52 :                 imprints = GDKzalloc(sizeof(Imprints));
     510          52 :                 if (imprints == NULL) {
     511           0 :                         bat_iterator_end(&bi);
     512           0 :                         if (unfix)
     513           0 :                                 BBPunfix(unfix);
     514           0 :                         return GDK_FAIL;
     515             :                 }
     516          52 :                 strconcat_len(imprints->imprints.filename,
     517             :                               sizeof(imprints->imprints.filename),
     518             :                               nme, ".timprints", NULL);
     519          52 :                 pages = (((size_t) bi.count * bi.width) + IMPS_PAGE - 1) / IMPS_PAGE;
     520          52 :                 imprints->imprints.farmid = BBPselectfarm(b->batRole, bi.type,
     521             :                                                           imprintsheap);
     522          52 :                 imprints->imprints.parentid = b->batCacheid;
     523             : 
     524             : #define SMP_SIZE 2048
     525          52 :                 s1 = BATsample(b, SMP_SIZE);
     526          52 :                 if (s1 == NULL) {
     527           0 :                         GDKfree(imprints);
     528           0 :                         bat_iterator_end(&bi);
     529           0 :                         if (unfix)
     530           0 :                                 BBPunfix(unfix);
     531           0 :                         return GDK_FAIL;
     532             :                 }
     533          52 :                 s2 = BATunique(b, s1);
     534          52 :                 if (s2 == NULL) {
     535           0 :                         BBPunfix(s1->batCacheid);
     536           0 :                         GDKfree(imprints);
     537           0 :                         bat_iterator_end(&bi);
     538           0 :                         if (unfix)
     539           0 :                                 BBPunfix(unfix);
     540           0 :                         return GDK_FAIL;
     541             :                 }
     542          52 :                 s3 = BATproject(s2, b);
     543          52 :                 if (s3 == NULL) {
     544           0 :                         BBPunfix(s1->batCacheid);
     545           0 :                         BBPunfix(s2->batCacheid);
     546           0 :                         GDKfree(imprints);
     547           0 :                         bat_iterator_end(&bi);
     548           0 :                         if (unfix)
     549           0 :                                 BBPunfix(unfix);
     550           0 :                         return GDK_FAIL;
     551             :                 }
     552          52 :                 s3->tkey = true;     /* we know is unique on tail now */
     553          52 :                 if (BATsort(&s4, NULL, NULL, s3, NULL, NULL, false, false, false) != GDK_SUCCEED) {
     554           0 :                         BBPunfix(s1->batCacheid);
     555           0 :                         BBPunfix(s2->batCacheid);
     556           0 :                         BBPunfix(s3->batCacheid);
     557           0 :                         GDKfree(imprints);
     558           0 :                         bat_iterator_end(&bi);
     559           0 :                         if (unfix)
     560           0 :                                 BBPunfix(unfix);
     561           0 :                         return GDK_FAIL;
     562             :                 }
     563             :                 /* s4 now is ordered and unique on tail */
     564          52 :                 assert(s4->tkey && s4->tsorted);
     565          52 :                 cnt = BATcount(s4);
     566          52 :                 imprints->bits = 64;
     567          52 :                 if (cnt <= 32)
     568          51 :                         imprints->bits = 32;
     569          51 :                 if (cnt <= 16)
     570          51 :                         imprints->bits = 16;
     571          51 :                 if (cnt <= 8)
     572          51 :                         imprints->bits = 8;
     573             : 
     574             :                 /* The heap we create here consists of four parts:
     575             :                  * bins, max 64 entries with bin boundaries, domain of b;
     576             :                  * stats, min/max/count for each bin, min/max are oid, and count BUN;
     577             :                  * imps, max one entry per "page", entry is "bits" wide;
     578             :                  * dict, max two entries per three "pages".
     579             :                  * In addition, we add some housekeeping entries at
     580             :                  * the start so that we can determine whether we can
     581             :                  * trust the imprints when encountered on startup (including
     582             :                  * a version number -- CURRENT VERSION is 2). */
     583          52 :                 MT_lock_set(&b->batIdxLock);
     584         104 :                 if (b->timprints != NULL ||
     585          52 :                     HEAPalloc(&imprints->imprints,
     586             :                               IMPRINTS_HEADER_SIZE * SIZEOF_SIZE_T + /* extra info */
     587          52 :                               64 * bi.width + /* bins */
     588             :                               64 * 3 * SIZEOF_BUN + /* {min,max,cnt}_bins */
     589          52 :                               pages * (imprints->bits / 8) + /* imps */
     590          52 :                               sizeof(uint64_t) + /* padding for alignment */
     591             :                               pages * sizeof(cchdc_t), /* dict */
     592             :                               1) != GDK_SUCCEED) {
     593           0 :                         MT_lock_unset(&b->batIdxLock);
     594           0 :                         bat_iterator_end(&bi);
     595           0 :                         GDKfree(imprints);
     596           0 :                         BBPunfix(s1->batCacheid);
     597           0 :                         BBPunfix(s2->batCacheid);
     598           0 :                         BBPunfix(s3->batCacheid);
     599           0 :                         BBPunfix(s4->batCacheid);
     600           0 :                         if (b->timprints != NULL) {
     601           0 :                                 if (unfix)
     602           0 :                                         BBPunfix(unfix);
     603           0 :                                 return GDK_SUCCEED; /* we were beaten to it */
     604             :                         }
     605           0 :                         if (unfix)
     606           0 :                                 BBPunfix(unfix);
     607           0 :                         return GDK_FAIL;
     608             :                 }
     609          52 :                 imprints->bins = imprints->imprints.base + IMPRINTS_HEADER_SIZE * SIZEOF_SIZE_T;
     610          52 :                 imprints->stats = (BUN *) ((char *) imprints->bins + 64 * bi.width);
     611          52 :                 imprints->imps = (void *) (imprints->stats + 64 * 3);
     612          52 :                 imprints->dict = (void *) ((uintptr_t) ((char *) imprints->imps + pages * (imprints->bits / 8) + sizeof(uint64_t)) & ~(sizeof(uint64_t) - 1));
     613             : 
     614          93 :                 switch (ATOMbasetype(bi.type)) {
     615           3 :                 case TYPE_bte:
     616         195 :                         FILL_HISTOGRAM(bte);
     617             :                         break;
     618           1 :                 case TYPE_sht:
     619          65 :                         FILL_HISTOGRAM(sht);
     620             :                         break;
     621          13 :                 case TYPE_int:
     622         845 :                         FILL_HISTOGRAM(int);
     623             :                         break;
     624          25 :                 case TYPE_lng:
     625        1625 :                         FILL_HISTOGRAM(lng);
     626             :                         break;
     627             : #ifdef HAVE_HGE
     628           5 :                 case TYPE_hge:
     629         325 :                         FILL_HISTOGRAM(hge);
     630             :                         break;
     631             : #endif
     632           3 :                 case TYPE_flt:
     633         195 :                         FILL_HISTOGRAM(flt);
     634             :                         break;
     635           2 :                 case TYPE_dbl:
     636         130 :                         FILL_HISTOGRAM(dbl);
     637             :                         break;
     638             :                 default:
     639             :                         /* should never reach here */
     640           0 :                         MT_UNREACHABLE();
     641             :                 }
     642             : 
     643          52 :                 imprints_create(b, &bi,
     644             :                                 imprints->bins,
     645             :                                 imprints->stats,
     646          52 :                                 imprints->bits,
     647             :                                 imprints->imps,
     648             :                                 &imprints->impcnt,
     649          52 :                                 imprints->dict,
     650             :                                 &imprints->dictcnt);
     651          52 :                 assert(imprints->impcnt <= pages);
     652          52 :                 assert(imprints->dictcnt <= pages);
     653             : #ifndef NDEBUG
     654          52 :                 memset((char *) imprints->imps + imprints->impcnt * (imprints->bits / 8), 0, (char *) imprints->dict - ((char *) imprints->imps + imprints->impcnt * (imprints->bits / 8)));
     655             : #endif
     656          52 :                 imprints->imprints.free = (size_t) ((char *) ((cchdc_t *) imprints->dict + imprints->dictcnt) - imprints->imprints.base);
     657             :                 /* add info to heap for when they become persistent */
     658          52 :                 ((size_t *) imprints->imprints.base)[0] = (size_t) (imprints->bits);
     659          52 :                 ((size_t *) imprints->imprints.base)[1] = (size_t) imprints->impcnt;
     660          52 :                 ((size_t *) imprints->imprints.base)[2] = (size_t) imprints->dictcnt;
     661          52 :                 ((size_t *) imprints->imprints.base)[3] = (size_t) bi.count;
     662          52 :                 imprints->imprints.dirty = true;
     663          52 :                 MT_lock_set(&b->theaplock);
     664          52 :                 if (b->batCount != bi.count) {
     665             :                         /* bat changed under our feet, can't use imprints */
     666           0 :                         MT_lock_unset(&b->theaplock);
     667           0 :                         MT_lock_unset(&b->batIdxLock);
     668           0 :                         bat_iterator_end(&bi);
     669           0 :                         HEAPfree(&imprints->imprints, true);
     670           0 :                         GDKfree(imprints);
     671           0 :                         BBPunfix(s1->batCacheid);
     672           0 :                         BBPunfix(s2->batCacheid);
     673           0 :                         BBPunfix(s3->batCacheid);
     674           0 :                         BBPunfix(s4->batCacheid);
     675           0 :                         GDKerror("Imprints creation aborted due to concurrent change to bat\n");
     676           0 :                         TRC_DEBUG(ACCELERATOR, "failed imprints construction: bat %s changed, " LLFMT " usec\n", BATgetId(b), GDKusec() - t0);
     677           0 :                         if (unfix)
     678           0 :                                 BBPunfix(unfix);
     679           0 :                         return GDK_FAIL;
     680             :                 }
     681          52 :                 ATOMIC_INIT(&imprints->imprints.refs, 1);
     682          52 :                 b->timprints = imprints;
     683          52 :                 MT_lock_unset(&b->theaplock);
     684          52 :                 if (BBP_status(b->batCacheid) & BBPEXISTING &&
     685          83 :                     !b->theap->dirty &&
     686          82 :                     !GDKinmemory(bi.h->farmid) &&
     687          41 :                     !GDKinmemory(imprints->imprints.farmid)) {
     688          41 :                         MT_Id tid;
     689          41 :                         BBPfix(b->batCacheid);
     690          41 :                         char name[MT_NAME_LEN];
     691          41 :                         snprintf(name, sizeof(name), "impssync%d", b->batCacheid);
     692          41 :                         if (MT_create_thread(&tid, BATimpsync, b,
     693             :                                              MT_THR_DETACHED, name) < 0)
     694           0 :                                 BBPunfix(b->batCacheid);
     695             :                 }
     696             :         }
     697             : 
     698          52 :         TRC_DEBUG(ACCELERATOR, "%s: imprints construction " LLFMT " usec\n",
     699             :                   BATgetId(b), GDKusec() - t0);
     700          52 :         MT_lock_unset(&b->batIdxLock);
     701          52 :         bat_iterator_end(&bi);
     702             : 
     703             :         /* BBPUnfix tries to get the imprints lock which might lead to
     704             :          * a deadlock if those were unfixed earlier */
     705          52 :         if (s1) {
     706          52 :                 BBPunfix(s1->batCacheid);
     707          52 :                 BBPunfix(s2->batCacheid);
     708          52 :                 BBPunfix(s3->batCacheid);
     709          52 :                 BBPunfix(s4->batCacheid);
     710             :         }
     711          52 :         if (unfix)
     712           0 :                 BBPunfix(unfix);
     713             :         return GDK_SUCCEED;
     714             : }
     715             : 
     716             : #define getbin(TYPE,B)                          \
     717             :         do {                                    \
     718             :                 const TYPE val = * (TYPE *) v;  \
     719             :                 GETBIN(ret,val,B);              \
     720             :         } while (0)
     721             : 
     722             : int
     723           0 : IMPSgetbin(int tpe, bte bits, const char *restrict inbins, const void *restrict v)
     724             : {
     725           0 :         int ret = -1;
     726             : 
     727           0 :         switch (tpe) {
     728           0 :         case TYPE_bte: {
     729           0 :                 const bte *restrict bins = (bte *) inbins;
     730           0 :                 BINSIZE(bits, getbin, bte);
     731             :                 break;
     732             :         }
     733           0 :         case TYPE_sht: {
     734           0 :                 const sht *restrict bins = (sht *) inbins;
     735           0 :                 BINSIZE(bits, getbin, sht);
     736             :                 break;
     737             :         }
     738           0 :         case TYPE_int: {
     739           0 :                 const int *restrict bins = (int *) inbins;
     740           0 :                 BINSIZE(bits, getbin, int);
     741             :                 break;
     742             :         }
     743           0 :         case TYPE_lng: {
     744           0 :                 const lng *restrict bins = (lng *) inbins;
     745           0 :                 BINSIZE(bits, getbin, lng);
     746             :                 break;
     747             :         }
     748             : #ifdef HAVE_HGE
     749           0 :         case TYPE_hge: {
     750           0 :                 const hge *restrict bins = (hge *) inbins;
     751           0 :                 BINSIZE(bits, getbin, hge);
     752             :                 break;
     753             :         }
     754             : #endif
     755           0 :         case TYPE_flt: {
     756           0 :                 const flt *restrict bins = (flt *) inbins;
     757           0 :                 BINSIZE(bits, getbin, flt);
     758             :                 break;
     759             :         }
     760           0 :         case TYPE_dbl: {
     761           0 :                 const dbl *restrict bins = (dbl *) inbins;
     762           0 :                 BINSIZE(bits, getbin, dbl);
     763             :                 break;
     764             :         }
     765             :         default:
     766           0 :                 MT_UNREACHABLE();
     767             :         }
     768           0 :         return ret;
     769             : }
     770             : 
     771             : lng
     772     6806144 : IMPSimprintsize(BAT *b)
     773             : {
     774     6806144 :         lng sz = 0;
     775     6806144 :         MT_lock_set(&b->batIdxLock);
     776     6801729 :         if (b->timprints && b->timprints != (Imprints *) 1) {
     777           0 :                 sz = (lng) b->timprints->imprints.free;
     778             :         }
     779     6801729 :         MT_lock_unset(&b->batIdxLock);
     780     6803787 :         return sz;
     781             : }
     782             : 
     783             : void
     784    77788053 : IMPSdestroy(BAT *b)
     785             : {
     786    77788053 :         MT_lock_set(&b->batIdxLock);
     787    77761965 :         if (b->timprints == (Imprints *) 1) {
     788           0 :                 b->timprints = NULL;
     789           0 :                 GDKunlink(BBPselectfarm(b->batRole, b->ttype, imprintsheap),
     790             :                           BATDIR,
     791           0 :                           BBP_physical(b->batCacheid),
     792             :                           "timprints");
     793    77761965 :         } else if (b->timprints != NULL) {
     794          48 :                 IMPSdecref(b->timprints, b->timprints->imprints.parentid == b->batCacheid);
     795          48 :                 b->timprints = NULL;
     796             :         }
     797    77761965 :         MT_lock_unset(&b->batIdxLock);
     798    77766816 : }
     799             : 
     800             : /* free the memory associated with the imprints, do not remove the
     801             :  * heap files; indicate that imprints are available on disk by setting
     802             :  * the imprints pointer to 1 */
     803             : void
     804      352924 : IMPSfree(BAT *b)
     805             : {
     806      352924 :         Imprints *imprints;
     807             : 
     808      352924 :         MT_lock_set(&b->batIdxLock);
     809      352923 :         imprints = b->timprints;
     810      352923 :         if (imprints != NULL && imprints != (Imprints *) 1) {
     811           4 :                 if (GDKinmemory(imprints->imprints.farmid)) {
     812           0 :                         b->timprints = NULL;
     813           0 :                         IMPSdecref(imprints, imprints->imprints.parentid == b->batCacheid);
     814             :                 } else {
     815           4 :                         if (imprints->imprints.parentid == b->batCacheid)
     816           4 :                                 b->timprints = (Imprints *) 1;
     817             :                         else
     818           0 :                                 b->timprints = NULL;
     819           4 :                         IMPSdecref(imprints, false);
     820             :                 }
     821             :         }
     822      352923 :         MT_lock_unset(&b->batIdxLock);
     823      352924 : }
     824             : 
     825             : void
     826          52 : IMPSdecref(Imprints *imprints, bool remove)
     827             : {
     828          52 :         TRC_DEBUG(ACCELERATOR, "Decrement ref count of %s\n", imprints->imprints.filename);
     829          52 :         if (remove)
     830          48 :                 ATOMIC_OR(&imprints->imprints.refs, HEAPREMOVE);
     831          52 :         ATOMIC_BASE_TYPE refs = ATOMIC_DEC(&imprints->imprints.refs);
     832          52 :         if ((refs & HEAPREFS) == 0) {
     833          52 :                 HEAPfree(&imprints->imprints, (bool) (refs & HEAPREMOVE));
     834          52 :                 GDKfree(imprints);
     835             :         }
     836          52 : }
     837             : 
     838             : void
     839           0 : IMPSincref(Imprints *imprints)
     840             : {
     841           0 :         TRC_DEBUG(ACCELERATOR, "Increment ref count of %s\n", imprints->imprints.filename);
     842           0 :         ATOMIC_INC(&imprints->imprints.refs);
     843           0 : }
     844             : 
     845             : #ifndef NDEBUG
     846             : /* never called, useful for debugging */
     847             : 
     848             : #define IMPSPRNTMASK(T, B)                                              \
     849             :         do {                                                            \
     850             :                 uint##B##_t *restrict im = (uint##B##_t *) imprints->imps; \
     851             :                 for (j = 0; j < imprints->bits; j++)                      \
     852             :                         s[j] = IMPSisSet(B, im[icnt], j) ? 'x' : '.';   \
     853             :                 s[j] = '\0';                                            \
     854             :         } while (0)
     855             : 
     856             : /* function used for debugging */
     857             : void
     858           0 : IMPSprint(BAT *b)
     859             : {
     860           0 :         Imprints *imprints;
     861           0 :         cchdc_t *restrict d;
     862           0 :         char s[65];             /* max number of bits + 1 */
     863           0 :         BUN icnt, dcnt, l, pages;
     864           0 :         BUN *restrict min_bins, *restrict max_bins;
     865           0 :         BUN *restrict cnt_bins;
     866           0 :         bte j;
     867           0 :         int i;
     868             : 
     869           0 :         if (!BATcheckimprints(b)) {
     870           0 :                 printf("No imprint\n");
     871           0 :                 return;
     872             :         }
     873           0 :         imprints = b->timprints;
     874           0 :         d = (cchdc_t *) imprints->dict;
     875           0 :         min_bins = imprints->stats;
     876           0 :         max_bins = min_bins + 64;
     877           0 :         cnt_bins = max_bins + 64;
     878             : 
     879           0 :         printf("bits = %d, impcnt = " BUNFMT ", dictcnt = " BUNFMT "\n",
     880           0 :                imprints->bits, imprints->impcnt, imprints->dictcnt);
     881           0 :         printf("MIN\n");
     882           0 :         for (i = 0; i < imprints->bits; i++) {
     883           0 :                 printf("[ " BUNFMT " ]\n", min_bins[i]);
     884             :         }
     885             : 
     886           0 :         printf("MAX\n");
     887           0 :         for (i = 0; i < imprints->bits; i++) {
     888           0 :                 printf("[ " BUNFMT " ]\n", max_bins[i]);
     889             :         }
     890           0 :         printf("COUNT\n");
     891           0 :         for (i = 0; i < imprints->bits; i++) {
     892           0 :                 printf("[ " BUNFMT " ]\n", cnt_bins[i]);
     893             :         }
     894           0 :         for (dcnt = 0, icnt = 0, pages = 1; dcnt < imprints->dictcnt; dcnt++) {
     895           0 :                 if (d[dcnt].repeat) {
     896           0 :                         BINSIZE(imprints->bits, IMPSPRNTMASK, " ");
     897           0 :                         pages += d[dcnt].cnt;
     898           0 :                         printf("[ " BUNFMT " ]r %s\n", pages, s);
     899           0 :                         icnt++;
     900             :                 } else {
     901           0 :                         l = icnt + d[dcnt].cnt;
     902           0 :                         for (; icnt < l; icnt++) {
     903           0 :                                 BINSIZE(imprints->bits, IMPSPRNTMASK, " ");
     904           0 :                                 printf("[ " BUNFMT " ]  %s\n", pages++, s);
     905             :                         }
     906             :                 }
     907             :         }
     908           0 :         fflush(stdout);
     909             : }
     910             : #endif

Generated by: LCOV version 1.14