# HG changeset patch
# User Alistair Sutherland
# Date 1373988337 -3600
# Branch Feb2013
# Node ID 59a45f5e5c8875f02064bf9d6671c785336d4191
# Parent  ea5b874f5289c5c87997296b1bdb21456fbb2efd
Caching fixes

diff --git a/gdk/gdk_heap.c b/gdk/gdk_heap.c
--- a/gdk/gdk_heap.c
+++ b/gdk/gdk_heap.c
@@ -61,10 +61,13 @@
 
 /* The heap cache should reduce mmap/munmap calls which are very
  * expensive.  Instead we try to reuse mmap's. This however requires
- * file renames.  The cache has a limited size!
+ * file renames. 
+ * 
+ * The cache has a limited size, although the number of cache entries
+ * should never grow to be larger than the number of GDK threads.
+ * To disable heap caching initialize to -1.
  */
-
-#define HEAP_CACHE_SIZE 5
+static int HEAPcacheSize = 64;
 
 typedef struct heap_cache_e {
 	void *base;
@@ -84,27 +87,28 @@
 void
 HEAPcacheInit(void)
 {
-#if HEAP_CACHE_SIZE > 0
-	if (!hc) {
-		int i;
+	if (HEAPcacheSize >= 0)
+	{
+		if (!hc) {
+			int i;
 
-		MT_lock_init(&HEAPcacheLock, "HEAPcache_init");
-		MT_lock_set(&HEAPcacheLock, "HEAPcache_init");
-		hc = (heap_cache *) GDKmalloc(sizeof(heap_cache));
-		hc->used = 0;
-		hc->sz = HEAP_CACHE_SIZE;
-		hc->hc = (heap_cache_e *) GDKmalloc(sizeof(heap_cache_e) * hc->sz);
-		GDKcreatedir(HCDIR DIR_SEP_STR);
-		/* clean old leftovers */
-		for (i = 0; i < HEAP_CACHE_SIZE; i++) {
-			char fn[PATHLENGTH];
+			MT_lock_init(&HEAPcacheLock, "HEAPcache_init");
+			MT_lock_set(&HEAPcacheLock, "HEAPcache_init");
+			hc = (heap_cache *) GDKmalloc(sizeof(heap_cache));
+			hc->used = 0;
+			hc->sz = HEAPcacheSize;
+			hc->hc = (heap_cache_e *) GDKmalloc(sizeof(heap_cache_e) * hc->sz);
+			GDKcreatedir(HCDIR DIR_SEP_STR);
+			/* clean old leftovers */
+			for (i = 0; i < HEAPcacheSize; i++) {
+				char fn[PATHLENGTH];
 
-			snprintf(fn, PATHLENGTH, "%d", i);
-			GDKunlink(HCDIR, fn, NULL);
+				snprintf(fn, PATHLENGTH, "%d", i);
+				GDKunlink(HCDIR, fn, NULL);
+			}
+			MT_lock_unset(&HEAPcacheLock, "HEAPcache_init");
 		}
-		MT_lock_unset(&HEAPcacheLock, "HEAPcache_init");
 	}
-#endif
 }
 
 static int
@@ -114,7 +118,7 @@
 
 
 	MT_lock_set(&HEAPcacheLock, "HEAPcache_init");
-	if (hc && free_file && fn && storage == STORE_MMAP && hc->used < hc->sz) {
+	if (hc && free_file && fn && storage == STORE_MMAP && hc->used < hc->sz - 1) {
 		heap_cache_e *e = hc->hc + hc->used;
 
 		e->base = base;
@@ -149,69 +153,85 @@
 
 	*maxsz = (1 + (*maxsz >> 16)) << 16;	/* round up to 64K */
 	MT_lock_set(&HEAPcacheLock, "HEAPcache_init");
-	if (hc && mode == STORE_MMAP && hc->used < hc->sz) {
-		HEAPDEBUG fprintf(stderr, "#HEAPcacheFind (%s)" SZFMT " %d %d\n", fn, *maxsz, (int) mode, hc->used);
+	if (hc && mode == STORE_MMAP && hc->used)
+    {
+        int i;
+        heap_cache_e *e = NULL;
+        int curdiffsz = 0;
 
-		if (hc->used) {
-			int i;
-			heap_cache_e *e = NULL;
-			size_t cursz = 0;
+        HEAPDEBUG fprintf(stderr, "#HEAPcacheFind (%s)" SZFMT " %d %d\n", fn, *maxsz, (int) mode, hc->used);
+        
+        /* find best match: prefer smallest larger
+         * than or equal to requested, otherwise
+         * largest smaller than requested */
+        for (i = 0; i < hc->used; i++)
+        {
+            int diffsz = hc->hc[i].maxsz - *maxsz;
+            if (e == NULL ||
+               (diffsz >= 0 && curdiffsz > 0 && diffsz < curdiffsz) ||
+               (diffsz >= 0 && curdiffsz < 0) ||
+               (diffsz <= 0 && curdiffsz < 0 && diffsz > curdiffsz))
+            {
+                e = hc->hc + i;
+                curdiffsz = diffsz;
+            }
+        }
+        if (e != NULL && e->maxsz > *maxsz && (e->maxsz - *maxsz) / *maxsz > 0.3)
+        {
+            /* cached heap too large (>30% larger than required) ...should we add truncate file? */
+            e = NULL;
+        }
+        if (e != NULL && e->maxsz < *maxsz)
+        {
+            /* cached heap too small, resize file */
+            long_str fn;
+            void *base;
 
-			/* find best match: prefer smallest larger
-			 * than or equal to requested, otherwise
-			 * largest smaller than requested */
-			for (i = 0; i < hc->used; i++) {
-				if ((hc->hc[i].maxsz >= *maxsz &&
-				     (e == NULL || hc->hc[i].maxsz < cursz)) ||
-				    (hc->hc[i].maxsz < *maxsz &&
-				     cursz < *maxsz &&
-				     hc->hc[i].maxsz > cursz)) {
-					e = hc->hc + i;
-					cursz = e->maxsz;
-				}
-			}
-			if (e != NULL && e->maxsz < *maxsz) {
-				/* resize file ? */
-				long_str fn;
+            GDKfilepath(fn, "HC", e->fn, NULL);
+            base = GDKload(fn, NULL, *maxsz, *maxsz, STORE_MMAP);
+            if(base != NULL)
+            {
+                GDKmunmap(e->base, e->maxsz);
+                e->base = base;
+                e->maxsz = *maxsz;
+            }
+            else
+            {
+                /* extending may have
+                 * failed */
+                e = NULL;
+            }
+        }
+        if (e != NULL)
+        {
+            /* move cached heap to its new location */
+            base = e->base;
+            *maxsz = e->maxsz;
+            if (GDKmove(HCDIR, e->fn, NULL, BATDIR, fn, NULL) < 0)
+            {
+                /* try to create the directory, if
+                 * that was the problem */
+                char path[PATHLENGTH];
 
-				GDKfilepath(fn, HCDIR, e->fn, NULL);
-				if (GDKextend(fn, *maxsz) == 0) {
-					void *base = GDKload(fn, NULL, *maxsz, *maxsz, STORE_MMAP);
-					GDKmunmap(e->base, e->maxsz);
-					e->base = base;
-					e->maxsz = *maxsz;
-				} else {
-					/* extending may have
-					 * failed */
-					e = NULL;
-				}
-			}
-			if (e != NULL) {
-				/* move cached heap to its new location */
-				base = e->base;
-				*maxsz = e->maxsz;
-				if (GDKmove(HCDIR, e->fn, NULL, BATDIR, fn, NULL) < 0) {
-					/* try to create the directory, if
-					 * that was the problem */
-					char path[PATHLENGTH];
+                GDKfilepath(path, BATDIR, fn, NULL);
+                GDKcreatedir(path);
+                if (GDKmove(HCDIR, e->fn, NULL, BATDIR, fn, NULL) < 0)
+                    e = NULL;
+            }
+        }
+        if (e != NULL)
+        {
+            hc->used--;
+            i = (int) (e - hc->hc);
+            if (i < hc->used)
+            {
+                e->base = hc->hc[hc->used].base;
+                e->maxsz = hc->hc[hc->used].maxsz;
+                GDKmove(HCDIR, hc->hc[hc->used].fn, NULL, HCDIR, e->fn, NULL);
+            }
+        }
 
-					GDKfilepath(path, BATDIR, fn, NULL);
-					GDKcreatedir(path);
-					if (GDKmove(HCDIR, e->fn, NULL, BATDIR, fn, NULL) < 0)
-						e = NULL;
-				}
-			}
-			if (e != NULL) {
-				hc->used--;
-				i = (int) (e - hc->hc);
-				if (i < hc->used) {
-					e->base = hc->hc[hc->used].base;
-					e->maxsz = hc->hc[hc->used].maxsz;
-					GDKmove(HCDIR, hc->hc[hc->used].fn, NULL, HCDIR, e->fn, NULL);
-				}
-			}
-		}
-	}
+    }
 	MT_lock_unset(&HEAPcacheLock, "HEAPcache_init");
 	if (!base) {
 		int fd = GDKfdlocate(fn, "wb", NULL);
@@ -448,6 +468,27 @@
 				HEAPDEBUG fprintf(stderr, "#HEAPextend: converting malloced to %s mmapped heap\n", h->newstorage == STORE_MMAP ? "shared" : "privately");
 				/* try to allocate a memory-mapped
 				 * based heap */
+				if (!existing && h->newstorage == STORE_MMAP)
+				{
+					/* we are extending a malloced heap swapping over
+                     * to mmapped storage */
+					h->storage = STORE_MMAP;
+
+					h->filename = (char *) GDKmalloc(strlen(nme) + strlen(ext) + 2);
+					if (h->filename == NULL)
+						return -1;
+				    sprintf(h->filename, "%s.%s", nme, ext);
+					
+					GDKunlink(BATDIR, nme, ext);
+					h->base = HEAPcacheFind(&h->maxsize, h->filename, h->storage);
+
+					/* copy data to heap and free
+					 * old memory */
+					memcpy(h->base, bak.base, bak.free);
+					HEAPfree(&bak);
+					return 0;
+				}
+
 				if (HEAPload(h, nme, ext, FALSE) >= 0) {
 					/* copy data to heap and free
 					 * old memory */
diff --git a/gdk/gdk_utils.c b/gdk/gdk_utils.c
--- a/gdk/gdk_utils.c
+++ b/gdk/gdk_utils.c
@@ -924,8 +924,8 @@
 		MEMDEBUG THRprintf(GDKstdout, "alloc = " SZFMT " %+zd rss = " SZFMT " %+zd\n", cursize, memdiff, rss, rssdiff);
 		prevmem = cursize;
 		prevrss = rss;
-		if (memdiff >= 0 && rssdiff < -32 * (ssize_t) MT_pagesize()) {
-			BBPtrim(rss);
+        if (rss > 0.8 * MT_npages() * MT_pagesize()) {
+			BBPtrim((size_t)(rss - 0.8 * MT_npages() * MT_pagesize()));
 			highload = 1;
 		} else {
 			highload = 0;
