diff options
author | drh <drh@noemail.net> | 2007-11-29 18:36:49 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2007-11-29 18:36:49 +0000 |
commit | ace03d1b3a14f0ef244f8e3e1b7c1408a5918d2e (patch) | |
tree | cec7e83d92eef18d8c9122461aa1a08bf3159bfd /src | |
parent | 21de2e755c4645778bd4e53b252909272b2c491a (diff) | |
download | sqlite-ace03d1b3a14f0ef244f8e3e1b7c1408a5918d2e.tar.gz sqlite-ace03d1b3a14f0ef244f8e3e1b7c1408a5918d2e.zip |
Add the optional (and experimental) mmap() memory allocator in the
mem4.c module. (CVS 4581)
FossilOrigin-Name: cfd683ac80fd043343e0f0af90805058daa3818d
Diffstat (limited to 'src')
-rw-r--r-- | src/mem1.c | 5 | ||||
-rw-r--r-- | src/mem2.c | 4 | ||||
-rw-r--r-- | src/mem3.c | 6 | ||||
-rw-r--r-- | src/mem4.c | 398 |
4 files changed, 408 insertions, 5 deletions
diff --git a/src/mem1.c b/src/mem1.c index 9b221743c..60ffc9138 100644 --- a/src/mem1.c +++ b/src/mem1.c @@ -12,7 +12,7 @@ ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem1.c,v 1.13 2007/11/05 17:54:17 drh Exp $ +** $Id: mem1.c,v 1.14 2007/11/29 18:36:49 drh Exp $ */ /* @@ -20,7 +20,8 @@ ** used when no other memory allocator is specified using compile-time ** macros. */ -#if !defined(SQLITE_MEMDEBUG) && !defined(SQLITE_MEMORY_SIZE) +#if !defined(SQLITE_MEMDEBUG) && !defined(SQLITE_MEMORY_SIZE) \ + && !defined(SQLITE_MMAP_HEAP_SIZE) /* ** We will eventually construct multiple memory allocation subsystems diff --git a/src/mem2.c b/src/mem2.c index 643f78955..69a641a85 100644 --- a/src/mem2.c +++ b/src/mem2.c @@ -12,7 +12,7 @@ ** This file contains the C functions that implement a memory ** allocation subsystem for use by SQLite. ** -** $Id: mem2.c,v 1.17 2007/11/05 17:54:17 drh Exp $ +** $Id: mem2.c,v 1.18 2007/11/29 18:36:49 drh Exp $ */ /* @@ -20,7 +20,7 @@ ** SQLITE_MEMDEBUG macro is defined and SQLITE_OMIT_MEMORY_ALLOCATION ** is not defined. */ -#if defined(SQLITE_MEMDEBUG) && !defined(SQLITE_MEMORY_SIZE) +#if defined(SQLITE_MEMDEBUG) /* ** We will eventually construct multiple memory allocation subsystems diff --git a/src/mem3.c b/src/mem3.c index c006b5eeb..ea6da6718 100644 --- a/src/mem3.c +++ b/src/mem3.c @@ -20,7 +20,7 @@ ** This version of the memory allocation subsystem is used if ** and only if SQLITE_MEMORY_SIZE is defined. ** -** $Id: mem3.c,v 1.6 2007/11/07 15:13:25 drh Exp $ +** $Id: mem3.c,v 1.7 2007/11/29 18:36:49 drh Exp $ */ /* @@ -30,6 +30,10 @@ #if defined(SQLITE_MEMORY_SIZE) #include "sqliteInt.h" +#ifdef SQLITE_MEMDEBUG +# error cannot define both SQLITE_MEMDEBUG and SQLITE_MEMORY_SIZE +#endif + /* ** Maximum size (in Mem3Blocks) of a "small" chunk. */ diff --git a/src/mem4.c b/src/mem4.c new file mode 100644 index 000000000..7d861d44c --- /dev/null +++ b/src/mem4.c @@ -0,0 +1,398 @@ +/* +** 2007 August 14 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement a memory +** allocation subsystem for use by SQLite. +** +** $Id: mem4.c,v 1.1 2007/11/29 18:36:49 drh Exp $ +*/ + +/* +** This version of the memory allocator attempts to obtain memory +** from mmap() if the size of the allocation is close to the size +** of a virtual memory page. If the size of the allocation is different +** from the virtual memory page size, then ordinary malloc() is used. +** Ordinary malloc is also used if space allocated to mmap() is +** exhausted. +** +** Enable this memory allocation by compiling with -DSQLITE_MMAP_HEAP_SIZE=nnn +** where nnn is the maximum number of bytes of mmap-ed memory you want +** to support. This module may choose to use less memory than requested. +** +*/ +#if defined(SQLITE_MMAP_HEAP_SIZE) + +#if defined(SQLITE_MEMDEBUG) || defined(SQLITE_MEMORY_SIZE) +# error cannot use SQLITE_MMAP_HEAP_SIZE with either SQLITE_MEMDEBUG \ + or SQLITE_MEMORY_SIZE +#endif + +/* +** This is a test version of the memory allocator that attempts to +** use mmap() and madvise() for allocations and frees of approximately +** the virtual memory page size. +*/ +#include <sys/types.h> +#include <sys/mman.h> +#include <errno.h> +#include "sqliteInt.h" +#include <unistd.h> + + +/* +** All of the static variables used by this module are collected +** into a single structure named "mem". This is to keep the +** static variables organized and to reduce namespace pollution +** when this module is combined with other in the amalgamation. +*/ +static struct { + /* + ** The alarm callback and its arguments. The mem.mutex lock will + ** be held while the callback is running. Recursive calls into + ** the memory subsystem are allowed, but no new callbacks will be + ** issued. The alarmBusy variable is set to prevent recursive + ** callbacks. + */ + sqlite3_int64 alarmThreshold; + void (*alarmCallback)(void*, sqlite3_int64,int); + void *alarmArg; + int alarmBusy; + + /* + ** Mutex to control access to the memory allocation subsystem. + */ + sqlite3_mutex *mutex; + + /* + ** Current allocation and high-water mark. + */ + sqlite3_int64 nowUsed; + sqlite3_int64 mxUsed; + + /* + ** Current allocation and high-water marks for mmap allocated memory. + */ + sqlite3_int64 nowUsedMMap; + sqlite3_int64 mxUsedMMap; + + /* + ** Size of a single mmap page. Obtained from sysconf(). + */ + int szPage; + int mnPage; + + /* + ** The number of available mmap pages. + */ + int nPage; + + /* + ** Index of the first free page. 0 means no pages have been freed. + */ + int firstFree; + + /* First unused page on the top of the heap. + */ + int firstUnused; + + /* + ** Bulk memory obtained from from mmap(). + */ + char *mmapHeap; /* first byte of the heap */ + +} mem; + + +/* +** Enter the mutex mem.mutex. Allocate it if it is not already allocated. +** The mmap() region is initialized the first time this routine is called. +*/ +static void memsys4Enter(void){ + if( mem.mutex==0 ){ + mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); + } + sqlite3_mutex_enter(mem.mutex); +} + +/* +** Attempt to free memory to the mmap heap. This only works if +** the pointer p is within the range of memory addresses that +** comprise the mmap heap. Return 1 if the memory was freed +** successfully. Return 0 if the pointer is out of range. +*/ +static int mmapFree(void *p){ + char *z; + int idx, *a; + if( mem.mmapHeap==MAP_FAILED || mem.nPage==0 ){ + return 0; + } + z = (char*)p; + idx = (z - mem.mmapHeap)/mem.szPage; + if( idx<1 || idx>=mem.nPage ){ + return 0; + } + a = (int*)mem.mmapHeap; + a[idx] = a[mem.firstFree]; + mem.firstFree = idx; + mem.nowUsedMMap -= mem.szPage; + madvise(p, mem.szPage, MADV_DONTNEED); + return 1; +} + +/* +** Attempt to allocate nBytes from the mmap heap. Return a pointer +** to the allocated page. Or, return NULL if the allocation fails. +** +** The allocation will fail if nBytes is not the right size. +** Or, the allocation will fail if the mmap heap has been exhausted. +*/ +static void *mmapAlloc(int nBytes){ + int idx = 0; + if( nBytes>mem.szPage || nBytes<mem.mnPage ){ + return 0; + } + if( mem.nPage==0 ){ + mem.szPage = sysconf(_SC_PAGE_SIZE); + mem.mnPage = mem.szPage - mem.szPage/10; + mem.nPage = SQLITE_MMAP_HEAP_SIZE/mem.szPage; + if( mem.nPage * sizeof(int) > mem.szPage ){ + mem.nPage = mem.szPage/sizeof(int); + } + mem.mmapHeap = mmap(0, mem.szPage*mem.nPage, PROT_WRITE|PROT_READ, + MAP_ANONYMOUS|MAP_SHARED, -1, 0); + if( mem.mmapHeap==MAP_FAILED ){ + mem.firstUnused = errno; + }else{ + mem.firstUnused = 1; + mem.nowUsedMMap = mem.szPage; + } + } + if( mem.mmapHeap==MAP_FAILED ){ + return 0; + } + if( mem.firstFree ){ + int idx = mem.firstFree; + int *a = (int*)mem.mmapHeap; + mem.firstFree = a[idx]; + }else if( mem.firstUnused<mem.nPage ){ + idx = mem.firstUnused++; + } + if( idx ){ + mem.nowUsedMMap += mem.szPage; + if( mem.nowUsedMMap>mem.mxUsedMMap ){ + mem.mxUsedMMap = mem.nowUsedMMap; + } + return (void*)&mem.mmapHeap[idx*mem.szPage]; + }else{ + return 0; + } +} + +/* +** Release the mmap-ed memory region if it is currently allocated and +** is not in use. +*/ +static void mmapUnmap(void){ + if( mem.mmapHeap==MAP_FAILED ) return; + if( mem.nPage==0 ) return; + if( mem.nowUsedMMap>mem.szPage ) return; + munmap(mem.mmapHeap, mem.nPage*mem.szPage); + mem.nowUsedMMap = 0; + mem.nPage = 0; +} + + +/* +** Return the amount of memory currently checked out. +*/ +sqlite3_int64 sqlite3_memory_used(void){ + sqlite3_int64 n; + memsys4Enter(); + n = mem.nowUsed + mem.nowUsedMMap; + sqlite3_mutex_leave(mem.mutex); + return n; +} + +/* +** Return the maximum amount of memory that has ever been +** checked out since either the beginning of this process +** or since the most recent reset. +*/ +sqlite3_int64 sqlite3_memory_highwater(int resetFlag){ + sqlite3_int64 n; + memsys4Enter(); + n = mem.mxUsed + mem.mxUsedMMap; + if( resetFlag ){ + mem.mxUsed = mem.nowUsed; + mem.mxUsedMMap = mem.nowUsedMMap; + } + sqlite3_mutex_leave(mem.mutex); + return n; +} + +/* +** Change the alarm callback +*/ +int sqlite3_memory_alarm( + void(*xCallback)(void *pArg, sqlite3_int64 used,int N), + void *pArg, + sqlite3_int64 iThreshold +){ + memsys4Enter(); + mem.alarmCallback = xCallback; + mem.alarmArg = pArg; + mem.alarmThreshold = iThreshold; + sqlite3_mutex_leave(mem.mutex); + return SQLITE_OK; +} + +/* +** Trigger the alarm +*/ +static void sqlite3MemsysAlarm(int nByte){ + void (*xCallback)(void*,sqlite3_int64,int); + sqlite3_int64 nowUsed; + void *pArg; + if( mem.alarmCallback==0 || mem.alarmBusy ) return; + mem.alarmBusy = 1; + xCallback = mem.alarmCallback; + nowUsed = mem.nowUsed; + pArg = mem.alarmArg; + sqlite3_mutex_leave(mem.mutex); + xCallback(pArg, nowUsed, nByte); + sqlite3_mutex_enter(mem.mutex); + mem.alarmBusy = 0; +} + +/* +** Allocate nBytes of memory +*/ +static void *memsys4Malloc(int nBytes){ + sqlite3_int64 *p = 0; + if( mem.alarmCallback!=0 + && mem.nowUsed+mem.nowUsedMMap+nBytes>=mem.alarmThreshold ){ + sqlite3MemsysAlarm(nBytes); + } + if( (p = mmapAlloc(nBytes))==0 ){ + p = malloc(nBytes+8); + if( p==0 ){ + sqlite3MemsysAlarm(nBytes); + p = malloc(nBytes+8); + } + if( p ){ + p[0] = nBytes; + p++; + mem.nowUsed += nBytes; + if( mem.nowUsed>mem.mxUsed ){ + mem.mxUsed = mem.nowUsed; + } + } + } + return (void*)p; +} + +/* +** Return the size of a memory allocation +*/ +static int memsys4Size(void *pPrior){ + char *z = (char*)pPrior; + int idx = mem.nPage ? (z - mem.mmapHeap)/mem.szPage : 0; + int nByte; + if( idx>=1 && idx<mem.nPage ){ + nByte = mem.szPage; + }else{ + sqlite3_int64 *p = pPrior; + p--; + nByte = (int)*p; + } + return nByte; +} + +/* +** Free memory. +*/ +static void memsys4Free(void *pPrior){ + sqlite3_int64 *p; + int nByte; + if( mmapFree(pPrior)==0 ){ + p = pPrior; + p--; + nByte = (int)*p; + mem.nowUsed -= nByte; + free(p); + if( mem.nowUsed==0 ){ + mmapUnmap(); + } + } +} + +/* +** Allocate nBytes of memory +*/ +void *sqlite3_malloc(int nBytes){ + sqlite3_int64 *p = 0; + if( nBytes>0 ){ + memsys4Enter(); + p = memsys4Malloc(nBytes); + sqlite3_mutex_leave(mem.mutex); + } + return (void*)p; +} + +/* +** Free memory. +*/ +void sqlite3_free(void *pPrior){ + if( pPrior==0 ){ + return; + } + assert( mem.mutex!=0 ); + sqlite3_mutex_enter(mem.mutex); + memsys4Free(pPrior); + sqlite3_mutex_leave(mem.mutex); +} + + + +/* +** Change the size of an existing memory allocation +*/ +void *sqlite3_realloc(void *pPrior, int nBytes){ + int nOld; + sqlite3_int64 *p; + if( pPrior==0 ){ + return sqlite3_malloc(nBytes); + } + if( nBytes<=0 ){ + sqlite3_free(pPrior); + return 0; + } + nOld = memsys4Size(pPrior); + if( nBytes<=nOld && nBytes>=nOld-128 ){ + return pPrior; + } + assert( mem.mutex!=0 ); + sqlite3_mutex_enter(mem.mutex); + p = memsys4Malloc(nBytes); + if( p ){ + if( nOld<nBytes ){ + memcpy(p, pPrior, nOld); + }else{ + memcpy(p, pPrior, nBytes); + } + memsys4Free(pPrior); + } + assert( mem.mutex!=0 ); + sqlite3_mutex_leave(mem.mutex); + return (void*)p; +} + +#endif /* !SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */ |