Changeset: e27322d4726e for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=e27322d4726e
Modified Files:
        gdk/gdk_atoms.c
        gdk/gdk_batop.c
        gdk/gdk_private.h
Branch: Oct2014
Log Message:

Try to be cleverer when appending string heaps.
See comments in and around insert_string_bat in gdk_batop.c.


diffs (truncated from 507 to 300 lines):

diff --git a/gdk/gdk_atoms.c b/gdk/gdk_atoms.c
--- a/gdk/gdk_atoms.c
+++ b/gdk/gdk_atoms.c
@@ -970,10 +970,6 @@ strHash(const char *s)
        return res;
 }
 
-/* if at least (2*SIZEOF_BUN), also store length (heaps are then
- * incompatible) */
-#define EXTRALEN ((SIZEOF_BUN + GDK_VARALIGN - 1) & ~(GDK_VARALIGN - 1))
-
 void
 strCleanHash(Heap *h, int rebuild)
 {
diff --git a/gdk/gdk_batop.c b/gdk/gdk_batop.c
--- a/gdk/gdk_batop.c
+++ b/gdk/gdk_batop.c
@@ -41,11 +41,12 @@
                }                                                       \
        } while (0)
 
-/*
- * BAT insert/delete/replace
- * The content of a BAT can be appended to (removed from) another
- * using BATins (BATdel).
- */
+/* We try to be clever when appending one string bat to another.
+ * First of all, we try to actually share the string heap so that we
+ * don't need an extra copy, and if that can't be done, we see whether
+ * it makes sense to just quickly copy the whole string heap instead
+ * of inserting individual strings.  See the comments in the code for
+ * more information. */
 static BAT *
 insert_string_bat(BAT *b, BAT *n, int append, int force)
 {
@@ -54,20 +55,17 @@ insert_string_bat(BAT *b, BAT *n, int ap
        size_t toff = ~(size_t) 0;      /* tail offset */
        BUN p, q;               /* loop variables */
        oid o = 0;              /* in case we're appending */
-       ptr hp, tp;             /* head and tail value pointers */
+       const void *hp, *tp;    /* head and tail value pointers */
        unsigned char tbv;      /* tail value-as-bte */
-       const unsigned char *tbp = NULL;        /* tail value-as-bte */
        unsigned short tsv;     /* tail value-as-sht */
-       const unsigned short *tsp = NULL;       /* tail value-as-sht */
 #if SIZEOF_VAR_T == 8
        unsigned int tiv;       /* tail value-as-int */
-       const unsigned int *tip = NULL; /* tail value-as-int */
 #endif
        var_t v;                /* value */
-       const var_t *tvp = NULL;        /* value */
-       int ntw, btw;           /* shortcuts for {b,n}->t->width */
+       int ntw, btw;           /* shortcuts for {b,n}->T->width */
+       size_t off;             /* offset within n's string heap */
 
-       assert(b->H->type == TYPE_void || b->H->type == TYPE_oid);
+       assert(b->htype == TYPE_void || b->htype == TYPE_oid);
        if (n->batCount == 0)
                return b;
        ni = bat_iterator(n);
@@ -75,7 +73,7 @@ insert_string_bat(BAT *b, BAT *n, int ap
        ntw = n->T->width;
        hp = NULL;
        tp = NULL;
-       if (b->H->type != TYPE_void) {
+       if (append && b->htype != TYPE_void) {
                hp = &o;
                o = MAXoid(b);
        }
@@ -85,87 +83,202 @@ insert_string_bat(BAT *b, BAT *n, int ap
            !GDK_ELIMDOUBLES(n->T->vheap) &&
            b->T->vheap->hashash == n->T->vheap->hashash &&
            /* if needs to be kept unique, take slow path */
-           (b->tkey & BOUND2BTRUE) == 0 &&
-           /* if view, only copy if significant part of parent is used */
-           (VIEWtparent(n) == 0 ||
-            BATcapacity(BBP_cache(VIEWtparent(n))) < 2 * BATcount(n))) {
-               /* append string heaps */
-               toff = b->batCount == 0 ? 0 : b->T->vheap->free;
-               /* make sure we get alignment right */
-               toff = (toff + GDK_VARALIGN - 1) & ~(GDK_VARALIGN - 1);
-               assert(((toff >> GDK_VARSHIFT) << GDK_VARSHIFT) == toff);
-               /* if in "force" mode, the heap may be shared when
-                * memory mapped */
-               if (HEAPextend(b->T->vheap, toff + n->T->vheap->size, force) < 
0) {
-                       toff = ~(size_t) 0;
-                       goto bunins_failed;
+           (b->tkey & BOUND2BTRUE) == 0) {
+               if (b->S->role == TRANSIENT) {
+                       /* If b is in the transient farm (i.e. b will
+                        * never become persistent), we try some
+                        * clever tricks to avoid copying:
+                        * - if b is empty, we just let is share the
+                         *   string heap with n;
+                        * - otherwise, if b's string heap and n's
+                         *   string heap are the same (i.e. shared),
+                         *   we leave it that way;
+                        * - otherwise, if b shares its string heap
+                         *   with some other bat, we materialize it
+                         *   and we will have to copy strings.
+                        */
+                       bat bid = abs(b->batCacheid);
+
+                       if (b->batCount == 0) {
+                               if (b->T->vheap->parentid != bid) {
+                                       BBPunshare(b->T->vheap->parentid);
+                               } else {
+                                       HEAPfree(b->T->vheap, 1);
+                                       GDKfree(b->T->vheap);
+                               }
+                               BBPshare(n->T->vheap->parentid);
+                               b->T->vheap = n->T->vheap;
+                               toff = 0;
+                       } else if (b->T->vheap->parentid == 
n->T->vheap->parentid) {
+                               toff = 0;
+                       } else if (b->T->vheap->parentid != bid) {
+                               Heap *h = GDKzalloc(sizeof(Heap));
+                               if (h == NULL)
+                                       return NULL;
+                               h->parentid = bid;
+                               h->farmid = BBPselectfarm(b->batRole, TYPE_str, 
varheap);
+                               if (b->T->vheap->filename) {
+                                       char *nme = BBP_physical(b->batCacheid);
+                                       h->filename = GDKfilepath(NOFARM, NULL, 
nme, "theap");
+                                       if (h->filename == NULL) {
+                                               GDKfree(h);
+                                               return NULL;
+                                       }
+                               }
+                               if (HEAPcopy(h, b->T->vheap) < 0) {
+                                       HEAPfree(h, 1);
+                                       GDKfree(h);
+                                       return NULL;
+                               }
+                               BBPunshare(b->T->vheap->parentid);
+                               b->T->vheap = h;
+                       }
                }
-               memcpy(b->T->vheap->base + toff, n->T->vheap->base, 
n->T->vheap->size);
-               b->T->vheap->free = toff + n->T->vheap->free;
-               /* flush double-elimination hash table */
-               memset(b->T->vheap->base, 0, GDK_STRHASHSIZE);
-               if (b->T->width < SIZEOF_VAR_T &&
-                   ((size_t) 1 << 8 * b->T->width) < (b->T->width <= 2 ? 
(b->T->vheap->size >> GDK_VARSHIFT) - GDK_VAROFFSET : (b->T->vheap->size >> 
GDK_VARSHIFT))) {
-                       /* offsets aren't going to fit */
-                       if (GDKupgradevarheap(b->T, (var_t) (b->T->vheap->size 
>> GDK_VARSHIFT), 0, force) == GDK_FAIL) {
-                               toff = ~(size_t) 0;
-                               goto bunins_failed;
+               if (toff == ~(size_t) 0 && n->batCount > 1024) {
+                       /* If b and n aren't sharing their string
+                        * heaps, we try to determine whether to copy
+                        * n's whole string heap to the end of b's, or
+                        * whether we will insert each string from n
+                        * individually.  We do this by testing a
+                        * sample of n's strings and extrapolating
+                        * from that sample whether n uses a
+                        * significant part of its string heap for its
+                        * strings (i.e. whether there are many unused
+                        * strings in n's string heap).  If n doesn't
+                        * have many strings in the first place, we
+                        * skip this and just insert them all
+                        * individually.  We also check whether a
+                        * significant number of n's strings happen to
+                        * have the same offset in b.  In the latter
+                        * case we also want to insert strings
+                        * individually, but reusing the string in b's
+                        * string heap. */
+                       int match = 0, i;
+                       size_t len = b->T->vheap->hashash ? 1024 * EXTRALEN : 0;
+                       for (i = 0; i < 1024; i++) {
+                               p = BUNfirst(n) + (BUN) (((double) rand() / 
RAND_MAX) * (BATcount(n) - 1));
+                               off = BUNtvaroff(ni, p);
+                               if (off < b->T->vheap->free &&
+                                   strcmp(b->T->vheap->base + off, 
n->T->vheap->base + off) == 0 &&
+                                   (!b->T->vheap->hashash ||
+                                    ((BUN *) (b->T->vheap->base + off))[-1] == 
(n->T->vheap->hashash ? ((BUN *) (n->T->vheap->base + off))[-1] : 
strHash(n->T->vheap->base + off))))
+                                       match++;
+                               len += (strlen(n->T->vheap->base + off) + 8) & 
~7;
                        }
-                       btw = b->T->width;
+                       if (match < 768 && (size_t) (BATcount(n) * (double) len 
/ 1024) >= n->T->vheap->free / 2) {
+                               /* append string heaps */
+                               toff = b->batCount == 0 ? 0 : b->T->vheap->free;
+                               /* make sure we get alignment right */
+                               toff = (toff + GDK_VARALIGN - 1) & 
~(GDK_VARALIGN - 1);
+                               assert(((toff >> GDK_VARSHIFT) << GDK_VARSHIFT) 
== toff);
+                               /* if in "force" mode, the heap may be shared 
when
+                                * memory mapped */
+                               if (HEAPextend(b->T->vheap, toff + 
n->T->vheap->size, force) < 0) {
+                                       toff = ~(size_t) 0;
+                                       goto bunins_failed;
+                               }
+                               memcpy(b->T->vheap->base + toff, 
n->T->vheap->base, n->T->vheap->size);
+                               b->T->vheap->free = toff + n->T->vheap->free;
+                               /* flush double-elimination hash table */
+                               memset(b->T->vheap->base, 0, GDK_STRHASHSIZE);
+                       }
                }
-               switch (btw) {
-               case 1:
-                       tt = TYPE_bte;
-                       break;
-               case 2:
-                       tt = TYPE_sht;
-                       break;
-#if SIZEOF_VAR_T == 8
-               case 4:
-                       tt = TYPE_int;
-                       break;
-#endif
-               default:
-                       tt = TYPE_var;
-                       break;
-               }
-               tbp = (const unsigned char *) Tloc(n, BUNfirst(n));
-               tsp = (const unsigned short *) Tloc(n, BUNfirst(n));
-#if SIZEOF_VAR_T == 8
-               tip = (const unsigned int *) Tloc(n, BUNfirst(n));
-#endif
-               tvp = (const var_t *) Tloc(n, BUNfirst(n));
-               b->T->varsized = 0;
-               b->T->type = tt;
-       }
-
-       BATloop(n, p, q) {
-               if (!append)
-                       hp = b->H->type ? BUNhloc(ni, p) : NULL;
-               if (!append && b->H->type && !n->H->type) {
-                       o = n->H->seq;
-                       hp = &o;
-               }
-
                if (toff != ~(size_t) 0) {
-                       switch (ntw) {
+                       /* we only have to copy the offsets from n to
+                        * b, possibly with an offset (if toff != 0),
+                        * so set up some variables and set up b's
+                        * tail so that it looks like it's a fixed
+                        * size column.  Of course, we must make sure
+                        * first that the width of b's offset heap can
+                        * accommodate all values. */
+                       if (b->T->width < SIZEOF_VAR_T &&
+                           ((size_t) 1 << 8 * b->T->width) < (b->T->width <= 2 
? (b->T->vheap->size >> GDK_VARSHIFT) - GDK_VAROFFSET : (b->T->vheap->size >> 
GDK_VARSHIFT))) {
+                               /* offsets aren't going to fit, so
+                                * widen offset heap */
+                               if (GDKupgradevarheap(b->T, (var_t) 
(b->T->vheap->size >> GDK_VARSHIFT), 0, force) == GDK_FAIL) {
+                                       toff = ~(size_t) 0;
+                                       goto bunins_failed;
+                               }
+                               btw = b->T->width;
+                       }
+                       switch (btw) {
                        case 1:
-                               v = (var_t) *tbp + GDK_VAROFFSET;
-                               tbp++;
+                               tt = TYPE_bte;
+                               tp = &tbv;
                                break;
                        case 2:
-                               v = (var_t) *tsp + GDK_VAROFFSET;
-                               tsp++;
+                               tt = TYPE_sht;
+                               tp = &tsv;
                                break;
 #if SIZEOF_VAR_T == 8
                        case 4:
-                               v = (var_t) *tip;
-                               tip++;
+                               tt = TYPE_int;
+                               tp = &tiv;
                                break;
 #endif
                        default:
-                               v = *tvp;
-                               tvp++;
+                               tt = TYPE_var;
+                               tp = &v;
+                               break;
+                       }
+                       b->tvarsized = 0;
+                       b->ttype = tt;
+               }
+       }
+       if (!append) {
+               if (b->htype == TYPE_void)
+                       hp = NULL;
+               else if (n->htype == TYPE_void) {
+                       assert(b->htype == TYPE_oid);
+                       o = n->hseqbase;
+                       hp = &o;
+                       append = 1;
+               }
+       }
+       if (toff == 0 && ntw == btw && (b->htype == TYPE_void || !append)) {
+               /* we don't need to do any translation of offset
+                * values, nor do we need to do any calculations for
+                * the head column, so we can use fast memcpy */
+               memcpy(Tloc(b, BUNlast(b)), Tloc(n, BUNfirst(n)),
+                      BATcount(n) * ntw);
+               if (b->htype != TYPE_void) {
_______________________________________________
checkin-list mailing list
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to