Updates to mem6.c allocator. (CVS 5473)

FossilOrigin-Name: 43a4cae2acea33d1a17c0037516e9c27fb7e8e91
diff --git a/src/main.c b/src/main.c
index 8cd1d64..cf55878 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.481 2008/07/24 08:20:40 danielk1977 Exp $
+** $Id: main.c,v 1.482 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -269,6 +269,7 @@
 
 #if defined(SQLITE_ENABLE_MEMSYS6)
     case SQLITE_CONFIG_CHUNKALLOC: {
+      sqlite3Config.nSmall = va_arg(ap, int);
       sqlite3Config.m = *sqlite3MemGetMemsys6();
       break;
     }
diff --git a/src/mem1.c b/src/mem1.c
index 359ce5d..1a3a68e 100644
--- a/src/mem1.c
+++ b/src/mem1.c
@@ -17,7 +17,7 @@
 ** This file contains implementations of the low-level memory allocation
 ** routines specified in the sqlite3_mem_methods object.
 **
-** $Id: mem1.c,v 1.24 2008/07/24 08:20:40 danielk1977 Exp $
+** $Id: mem1.c,v 1.25 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -120,7 +120,7 @@
   return;
 }
 
-sqlite3_mem_methods *sqlite3MemGetDefault(void){
+const sqlite3_mem_methods *sqlite3MemGetDefault(void){
   static const sqlite3_mem_methods defaultMethods = {
      sqlite3MemMalloc,
      sqlite3MemFree,
diff --git a/src/mem2.c b/src/mem2.c
index 33c8cfe..f1425e8 100644
--- a/src/mem2.c
+++ b/src/mem2.c
@@ -19,7 +19,7 @@
 ** This file contains implementations of the low-level memory allocation
 ** routines specified in the sqlite3_mem_methods object.
 **
-** $Id: mem2.c,v 1.36 2008/07/24 23:34:07 drh Exp $
+** $Id: mem2.c,v 1.37 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -324,7 +324,7 @@
 }
 
 
-sqlite3_mem_methods *sqlite3MemGetDefault(void){
+const sqlite3_mem_methods *sqlite3MemGetDefault(void){
   static const sqlite3_mem_methods defaultMethods = {
      sqlite3MemMalloc,
      sqlite3MemFree,
diff --git a/src/mem6.c b/src/mem6.c
index bf1724c..7903c08 100644
--- a/src/mem6.c
+++ b/src/mem6.c
@@ -10,19 +10,52 @@
 **
 *************************************************************************
 **
-** $Id: mem6.c,v 1.2 2008/07/24 10:11:28 danielk1977 Exp $
+** This file contains an alternative memory allocation system for SQLite.
+** This system is implemented as a wrapper around the default memory
+** allocation system (usually the one found in mem1.c - system malloc).
+**
+** This system differentiates between requests for "small" allocations 
+** (by default those of 128 bytes or less) and "large" allocations (all
+** others). The 256 byte threshhold is configurable at runtime.
+**
+** All requests for large allocations are passed through to the
+** default memory allocation system.
+**
+** Requests for small allocations are met by allocating space within
+** one or more larger "chunks" of memory obtained from the default
+** memory allocation system. Chunks of memory are usually 64KB or 
+** larger. The algorithm used to manage space within each chunk is
+** the same as that used by mem5.c. 
+**
+** This strategy is designed to prevent the default memory allocation
+** system (usually the system malloc) from suffering from heap 
+** fragmentation. On some systems, heap fragmentation can cause a 
+** significant real-time slowdown.
+**
+** $Id: mem6.c,v 1.3 2008/07/25 08:49:00 danielk1977 Exp $
 */
 
 #ifdef SQLITE_ENABLE_MEMSYS6
 
+#include "sqliteInt.h"
+
 /*
-** Maximum size of any allocation is ((1<<LOGMAX)*Mem6Chunk.nAtom). Since
-** Mem6Chunk.nAtom is always at least 8, this is not really a practical
-** limitation.
+** Maximum size of any "small" allocation is ((1<<LOGMAX)*Mem6Chunk.nAtom).
+** Mem6Chunk.nAtom is always at least 8, so this is not a practical
+** limitation
 */
 #define LOGMAX 30
 
-#include "sqliteInt.h"
+/*
+** Default value for the "small" allocation size threshold.
+*/
+#define SMALL_MALLOC_DEFAULT_THRESHOLD 256
+
+/*
+** Minimum size for a memory chunk.
+*/
+#define MIN_CHUNKSIZE (1<<16)
+
 
 typedef struct Mem6Chunk Mem6Chunk;
 typedef struct Mem6Link Mem6Link;
@@ -278,12 +311,9 @@
 
 struct Mem6Global {
   sqlite3_mem_methods parent;     /* Used to allocate chunks */
-  int nChunkSize;                 /* Size of each chunk, in bytes. */
   int nMinAlloc;                  /* Minimum allowed allocation size */
-
+  int nThreshold;                 /* Allocs larger than this go to parent */
   sqlite3_mutex *mutex;
-
-  /* This data structure will be fixed... */
   Mem6Chunk *pChunk;              /* Singly linked list of all memory chunks */
 } mem6;
 
@@ -297,6 +327,22 @@
 }
 
 /*
+** Based on the number and size of the currently allocated chunks, return
+** the size of the next chunk to allocate, in bytes.
+*/
+static int nextChunkSize(void){
+  int iTotal = 0;
+  Mem6Chunk *p;
+  for(p=mem6.pChunk; p; p=p->pNext){
+    iTotal += mem6.parent.xSize((void *)p);
+  }
+  if( iTotal==0 ){
+    iTotal = MIN_CHUNKSIZE;
+  }
+  return iTotal;
+}
+
+/*
 ** The argument is a pointer that may or may not have been allocated from
 ** one of the Mem6Chunk objects managed within mem6. If it is, return
 ** a pointer to the owner chunk. If not, return 0.
@@ -323,7 +369,7 @@
   void *p = 0;
 
   mem6Enter();
-  if( nByte>=mem6.nChunkSize/3 ){
+  if( nByte>mem6.nThreshold ){
     p = mem6.parent.xMalloc(nByte);
   }else{
     for(pChunk=mem6.pChunk; !p && pChunk; pChunk=pChunk->pNext){
@@ -331,9 +377,10 @@
     }
   
     if( !p ){
-      p = mem6.parent.xMalloc(mem6.nChunkSize);
+      int iSize = nextChunkSize();
+      p = mem6.parent.xMalloc(iSize);
       if( p ){
-        pChunk = chunkInit((u8 *)p, mem6.nChunkSize, mem6.nMinAlloc);
+        pChunk = chunkInit((u8 *)p, iSize, mem6.nMinAlloc);
         pChunk->pNext = mem6.pChunk;
         mem6.pChunk = pChunk;
         p = chunkMalloc(pChunk, nByte);
@@ -398,10 +445,12 @@
 static int memsys6Init(void *pCtx){
   u8 bMemstat = sqlite3Config.bMemstat;
   mem6.parent = *sqlite3MemGetDefault();
-  mem6.nChunkSize = (1<<16);
   mem6.nMinAlloc = 16;
   mem6.pChunk = 0;
-
+  mem6.nThreshold = sqlite3Config.nSmall;
+  if( mem6.nThreshold<=0 ){
+    mem6.nThreshold = SMALL_MALLOC_DEFAULT_THRESHOLD;
+  }
   if( !bMemstat ){
     mem6.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
   }
@@ -419,6 +468,10 @@
 }
 
 static void memsys6Shutdown(void *pCtx){
+  if( mem6.parent.xShutdown ){
+    mem6.parent.xShutdown(mem6.parent.pAppData);
+  }
+  memset(&mem6, 0, sizeof(mem6));
 }
 
 /*
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 3a7f1a2..246ba6e 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -30,7 +30,7 @@
 ** the version number) and changes its name to "sqlite3.h" as
 ** part of the build process.
 **
-** @(#) $Id: sqlite.h.in,v 1.378 2008/07/24 08:20:40 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.379 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1157,8 +1157,7 @@
 #define SQLITE_CONFIG_MEMSTATUS     9  /* boolean */
 #define SQLITE_CONFIG_MUTEX        10  /* sqlite3_mutex_methods* */
 #define SQLITE_CONFIG_GETMUTEX     11  /* sqlite3_mutex_methods* */
-
-#define SQLITE_CONFIG_CHUNKALLOC   12  /* nil */
+#define SQLITE_CONFIG_CHUNKALLOC   12  /* int threshold */
 
 
 /*
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 28d6176..45cbdfd 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.744 2008/07/24 08:20:40 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.745 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1789,6 +1789,7 @@
   int isInit;                       /* True after initialization has finished */
   int isMallocInit;                 /* True after malloc is initialized */
   sqlite3_mutex *pInitMutex;        /* Mutex used by sqlite3_initialize() */
+  int nSmall;                       /* alloc size threshold used by mem6.c */
 };
 
 /*
@@ -1841,7 +1842,7 @@
 void *sqlite3PageMalloc(int);
 void sqlite3PageFree(void*);
 void sqlite3MemSetDefault(void);
-sqlite3_mem_methods *sqlite3MemGetDefault(void);
+const sqlite3_mem_methods *sqlite3MemGetDefault(void);
 const sqlite3_mem_methods *sqlite3MemGetMemsys5(void);
 const sqlite3_mem_methods *sqlite3MemGetMemsys3(void);
 const sqlite3_mem_methods *sqlite3MemGetMemsys6(void);
diff --git a/src/test_malloc.c b/src/test_malloc.c
index 30df034..ea5c2d6 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -13,7 +13,7 @@
 ** This file contains code used to implement test interfaces to the
 ** memory allocation subsystem.
 **
-** $Id: test_malloc.c,v 1.39 2008/07/24 08:20:40 danielk1977 Exp $
+** $Id: test_malloc.c,v 1.40 2008/07/25 08:49:00 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -967,11 +967,13 @@
   Tcl_Obj *CONST objv[]
 ){
   int rc;
-  if( objc!=1 ){
-    Tcl_WrongNumArgs(interp, 1, objv, "");
+  int nThreshold;
+  if( objc!=2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "THRESHOLD");
     return TCL_ERROR;
   }
-  rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC);
+  if( Tcl_GetIntFromObj(interp, objv[1], &nThreshold) ) return TCL_ERROR;
+  rc = sqlite3_config(SQLITE_CONFIG_CHUNKALLOC, nThreshold);
   Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
   return TCL_OK;
 }