Commit d16e01cf authored by Benjamin Peterson's avatar Benjamin Peterson

mmap obmalloc arenas so that they may be immediately returned to the system...

mmap obmalloc arenas so that they may be immediately returned to the system when unused (closes #20494)
parent 90ecc001
...@@ -9,6 +9,10 @@ What's New in Python 2.7.7? ...@@ -9,6 +9,10 @@ What's New in Python 2.7.7?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #20494: Ensure that free()d memory arenas are really released on POSIX
systems supporting anonymous memory mappings. Patch by Charles-François
Natali.
- Issue #17825: Cursor "^" is correctly positioned for SyntaxError and - Issue #17825: Cursor "^" is correctly positioned for SyntaxError and
IndentationError. IndentationError.
......
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
#ifdef WITH_PYMALLOC #ifdef WITH_PYMALLOC
#ifdef HAVE_MMAP
#include <sys/mman.h>
#ifdef MAP_ANONYMOUS
#define ARENAS_USE_MMAP
#endif
#endif
#ifdef WITH_VALGRIND #ifdef WITH_VALGRIND
#include <valgrind/valgrind.h> #include <valgrind/valgrind.h>
...@@ -75,7 +82,8 @@ static int running_on_valgrind = -1; ...@@ -75,7 +82,8 @@ static int running_on_valgrind = -1;
* Allocation strategy abstract: * Allocation strategy abstract:
* *
* For small requests, the allocator sub-allocates <Big> blocks of memory. * For small requests, the allocator sub-allocates <Big> blocks of memory.
* Requests greater than 256 bytes are routed to the system's allocator. * Requests greater than SMALL_REQUEST_THRESHOLD bytes are routed to the
* system's allocator.
* *
* Small requests are grouped in size classes spaced 8 bytes apart, due * Small requests are grouped in size classes spaced 8 bytes apart, due
* to the required valid alignment of the returned address. Requests of * to the required valid alignment of the returned address. Requests of
...@@ -107,10 +115,11 @@ static int running_on_valgrind = -1; ...@@ -107,10 +115,11 @@ static int running_on_valgrind = -1;
* 57-64 64 7 * 57-64 64 7
* 65-72 72 8 * 65-72 72 8
* ... ... ... * ... ... ...
* 241-248 248 30 * 497-504 504 62
* 249-256 256 31 * 505-512 512 63
* *
* 0, 257 and up: routed to the underlying allocator. * 0, SMALL_REQUEST_THRESHOLD + 1 and up: routed to the underlying
* allocator.
*/ */
/*==========================================================================*/ /*==========================================================================*/
...@@ -143,10 +152,13 @@ static int running_on_valgrind = -1; ...@@ -143,10 +152,13 @@ static int running_on_valgrind = -1;
* 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 256 * 1) ALIGNMENT <= SMALL_REQUEST_THRESHOLD <= 256
* 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT * 2) SMALL_REQUEST_THRESHOLD is evenly divisible by ALIGNMENT
* *
* Note: a size threshold of 512 guarantees that newly created dictionaries
* will be allocated from preallocated memory pools on 64-bit.
*
* Although not required, for better performance and space efficiency, * Although not required, for better performance and space efficiency,
* it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2. * it is recommended that SMALL_REQUEST_THRESHOLD is set to a power of 2.
*/ */
#define SMALL_REQUEST_THRESHOLD 256 #define SMALL_REQUEST_THRESHOLD 512
#define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT) #define NB_SMALL_SIZE_CLASSES (SMALL_REQUEST_THRESHOLD / ALIGNMENT)
/* /*
...@@ -174,15 +186,15 @@ static int running_on_valgrind = -1; ...@@ -174,15 +186,15 @@ static int running_on_valgrind = -1;
/* /*
* The allocator sub-allocates <Big> blocks of memory (called arenas) aligned * The allocator sub-allocates <Big> blocks of memory (called arenas) aligned
* on a page boundary. This is a reserved virtual address space for the * on a page boundary. This is a reserved virtual address space for the
* current process (obtained through a malloc call). In no way this means * current process (obtained through a malloc()/mmap() call). In no way this
* that the memory arenas will be used entirely. A malloc(<Big>) is usually * means that the memory arenas will be used entirely. A malloc(<Big>) is
* an address range reservation for <Big> bytes, unless all pages within this * usually an address range reservation for <Big> bytes, unless all pages within
* space are referenced subsequently. So malloc'ing big blocks and not using * this space are referenced subsequently. So malloc'ing big blocks and not
* them does not mean "wasting memory". It's an addressable range wastage... * using them does not mean "wasting memory". It's an addressable range
* wastage...
* *
* Therefore, allocating arenas with malloc is not optimal, because there is * Arenas are allocated with mmap() on systems supporting anonymous memory
* some address space wastage, but this is the most portable way to request * mappings to reduce heap fragmentation.
* memory from the system across various platforms.
*/ */
#define ARENA_SIZE (256 << 10) /* 256KB */ #define ARENA_SIZE (256 << 10) /* 256KB */
...@@ -440,6 +452,9 @@ static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = { ...@@ -440,6 +452,9 @@ static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = {
, PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55)
#if NB_SMALL_SIZE_CLASSES > 56 #if NB_SMALL_SIZE_CLASSES > 56
, PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63)
#if NB_SMALL_SIZE_CLASSES > 64
#error "NB_SMALL_SIZE_CLASSES should be less than 64"
#endif /* NB_SMALL_SIZE_CLASSES > 64 */
#endif /* NB_SMALL_SIZE_CLASSES > 56 */ #endif /* NB_SMALL_SIZE_CLASSES > 56 */
#endif /* NB_SMALL_SIZE_CLASSES > 48 */ #endif /* NB_SMALL_SIZE_CLASSES > 48 */
#endif /* NB_SMALL_SIZE_CLASSES > 40 */ #endif /* NB_SMALL_SIZE_CLASSES > 40 */
...@@ -577,7 +592,12 @@ new_arena(void) ...@@ -577,7 +592,12 @@ new_arena(void)
arenaobj = unused_arena_objects; arenaobj = unused_arena_objects;
unused_arena_objects = arenaobj->nextarena; unused_arena_objects = arenaobj->nextarena;
assert(arenaobj->address == 0); assert(arenaobj->address == 0);
#ifdef ARENAS_USE_MMAP
arenaobj->address = (uptr)mmap(NULL, ARENA_SIZE, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
#else
arenaobj->address = (uptr)malloc(ARENA_SIZE); arenaobj->address = (uptr)malloc(ARENA_SIZE);
#endif
if (arenaobj->address == 0) { if (arenaobj->address == 0) {
/* The allocation failed: return NULL after putting the /* The allocation failed: return NULL after putting the
* arenaobj back. * arenaobj back.
...@@ -1054,7 +1074,11 @@ PyObject_Free(void *p) ...@@ -1054,7 +1074,11 @@ PyObject_Free(void *p)
unused_arena_objects = ao; unused_arena_objects = ao;
/* Free the entire arena. */ /* Free the entire arena. */
#ifdef ARENAS_USE_MMAP
munmap((void *)ao->address, ARENA_SIZE);
#else
free((void *)ao->address); free((void *)ao->address);
#endif
ao->address = 0; /* mark unassociated */ ao->address = 0; /* mark unassociated */
--narenas_currently_allocated; --narenas_currently_allocated;
......
...@@ -10164,7 +10164,7 @@ for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \ ...@@ -10164,7 +10164,7 @@ for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime \ initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \
mremap nice pathconf pause plock poll pthread_init \ mremap nice pathconf pause plock poll pthread_init \
putenv readlink realpath \ putenv readlink realpath \
select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \
......
...@@ -2905,7 +2905,7 @@ AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \ ...@@ -2905,7 +2905,7 @@ AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime \ initgroups kill killpg lchmod lchown lstat mkfifo mknod mktime mmap \
mremap nice pathconf pause plock poll pthread_init \ mremap nice pathconf pause plock poll pthread_init \
putenv readlink realpath \ putenv readlink realpath \
select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \ select sem_open sem_timedwait sem_getvalue sem_unlink setegid seteuid \
......
...@@ -475,6 +475,9 @@ ...@@ -475,6 +475,9 @@
/* Define to 1 if you have the `mktime' function. */ /* Define to 1 if you have the `mktime' function. */
#undef HAVE_MKTIME #undef HAVE_MKTIME
/* Define to 1 if you have the `mmap' function. */
#undef HAVE_MMAP
/* Define to 1 if you have the `mremap' function. */ /* Define to 1 if you have the `mremap' function. */
#undef HAVE_MREMAP #undef HAVE_MREMAP
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment