PEP 445:
* add PyMemAllocatorDomain enum: PYALLOC_PYMEM_RAW, PYALLOC_PYMEM or PYALLOC_PYOBJECT * rename: - PyMemBlockAllocator structure => PyMemAllocator - PyMem_GetMappingAllocator() => PyObject_GetArenaAllocator() - PyMemMappingAllocator structure => PyObjectArenaAllocator - PyMem_SetMappingAllocator() => PyObject_SetArenaAllocator() * group get/set functions to only keep 2 functions: PyMem_GetAllocator() and PyMem_SetAllocator() * PyMem_RawMalloc(0) now calls malloc(1) to have a well defined behaviour * PYALLOC_PYMEM_RAW and PYALLOC_PYMEM are now using exactly the same allocator * Add more references for external libraries
This commit is contained in:
parent
f4a21c42e6
commit
682a7fe994
301
pep-0445.txt
301
pep-0445.txt
|
@ -40,18 +40,20 @@ Use cases:
|
||||||
Proposal
|
Proposal
|
||||||
========
|
========
|
||||||
|
|
||||||
API changes
|
New functions and new structure
|
||||||
-----------
|
-------------------------------
|
||||||
|
|
||||||
* Add new GIL-free (no need to hold the GIL) memory allocator functions:
|
* Add a new GIL-free (no need to hold the GIL) memory allocator:
|
||||||
|
|
||||||
- ``void* PyMem_RawMalloc(size_t size)``
|
- ``void* PyMem_RawMalloc(size_t size)``
|
||||||
- ``void* PyMem_RawRealloc(void *ptr, size_t new_size)``
|
- ``void* PyMem_RawRealloc(void *ptr, size_t new_size)``
|
||||||
- ``void PyMem_RawFree(void *ptr)``
|
- ``void PyMem_RawFree(void *ptr)``
|
||||||
- the behaviour of requesting zero bytes is not defined: return *NULL*
|
- The newly allocated memory will not have been initialized in any
|
||||||
or a distinct non-*NULL* pointer depending on the platform.
|
way.
|
||||||
|
- Requesting zero bytes returns a distinct non-*NULL* pointer if
|
||||||
|
possible, as if ``PyMem_Malloc(1)`` had been called instead.
|
||||||
|
|
||||||
* Add a new ``PyMemBlockAllocator`` structure::
|
* Add a new ``PyMemAllocator`` structure::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument
|
/* user context passed as the first argument
|
||||||
|
@ -66,69 +68,70 @@ API changes
|
||||||
|
|
||||||
/* release a memory block */
|
/* release a memory block */
|
||||||
void (*free) (void *ctx, void *ptr);
|
void (*free) (void *ctx, void *ptr);
|
||||||
} PyMemBlockAllocator;
|
} PyMemAllocator;
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of
|
* Add a new ``PyMemAllocatorDomain`` enum to choose the Python
|
||||||
``PyMem_RawMalloc()``, ``PyMem_RawRealloc()`` and ``PyMem_RawFree()``:
|
allocator domain. Domains:
|
||||||
|
|
||||||
- ``void PyMem_GetRawAllocator(PyMemBlockAllocator *allocator)``
|
- ``PYALLOC_PYMEM_RAW``: ``PyMem_RawMalloc()``, ``PyMem_RawRealloc()``
|
||||||
- ``void PyMem_SetRawAllocator(PyMemBlockAllocator *allocator)``
|
and ``PyMem_RawRealloc()``
|
||||||
- default allocator: ``malloc()``, ``realloc()``, ``free()``
|
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of
|
- ``PYALLOC_PYMEM``: ``PyMem_Malloc()``, ``PyMem_Realloc()`` and
|
||||||
``PyMem_Malloc()``, ``PyMem_Realloc()`` and ``PyMem_Free()``:
|
``PyMem_Realloc()``
|
||||||
|
|
||||||
- ``void PyMem_GetAllocator(PyMemBlockAllocator *allocator)``
|
- ``PYALLOC_PYOBJECT``: ``PyObject_Malloc()``, ``PyObject_Realloc()``
|
||||||
- ``void PyMem_SetAllocator(PyMemBlockAllocator *allocator)``
|
and ``PyObject_Realloc()``
|
||||||
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return
|
|
||||||
*NULL*: it would be treated as an error.
|
|
||||||
- default allocator: ``malloc()``, ``realloc()``, ``free()``;
|
|
||||||
``PyMem_Malloc(0)`` calls ``malloc(1)``
|
|
||||||
and ``PyMem_Realloc(NULL, 0)`` calls ``realloc(NULL, 1)``
|
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of
|
* Add new functions to get and set memory allocators:
|
||||||
``PyObject_Malloc()``, ``PyObject_Realloc()`` and
|
|
||||||
``PyObject_Free()``:
|
|
||||||
|
|
||||||
- ``void PyObject_GetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)``
|
||||||
- ``void PyObject_SetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)``
|
||||||
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return
|
- The new allocator must return a distinct non-*NULL* pointer when
|
||||||
*NULL*: it would be treated as an error.
|
requesting zero bytes
|
||||||
- default allocator: the *pymalloc* allocator
|
|
||||||
|
|
||||||
* Add a new ``PyMemMappingAllocator`` structure::
|
* Add a new ``PyObjectArenaAllocator`` structure::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument
|
/* user context passed as the first argument
|
||||||
to the 2 functions */
|
to the 2 functions */
|
||||||
void *ctx;
|
void *ctx;
|
||||||
|
|
||||||
/* allocate a memory mapping */
|
/* allocate an arena */
|
||||||
void* (*alloc) (void *ctx, size_t size);
|
void* (*alloc) (void *ctx, size_t size);
|
||||||
|
|
||||||
/* release a memory mapping */
|
/* release an arena */
|
||||||
void (*free) (void *ctx, void *ptr, size_t size);
|
void (*free) (void *ctx, void *ptr, size_t size);
|
||||||
} PyMemMappingAllocator;
|
} PyObjectArenaAllocator;
|
||||||
|
|
||||||
* Add a new function to get and set the memory mapping allocator:
|
* Add new functions to get and set the arena allocator used by
|
||||||
|
*pymalloc*:
|
||||||
|
|
||||||
- ``void PyMem_GetMappingAllocator(PyMemMappingAllocator *allocator)``
|
- ``void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)``
|
||||||
- ``void PyMem_SetMappingAllocator(PyMemMappingAllocator *allocator)``
|
- ``void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)``
|
||||||
- Currently, this allocator is only used internally by *pymalloc* to
|
|
||||||
allocate arenas.
|
|
||||||
|
|
||||||
* Add a new function to setup the builtin Python debug hooks when memory
|
* Add a new function to setup the builtin Python debug hooks when a
|
||||||
allocators are replaced:
|
memory allocator is replaced:
|
||||||
|
|
||||||
- ``void PyMem_SetupDebugHooks(void)``
|
- ``void PyMem_SetupDebugHooks(void)``
|
||||||
- the function does nothing is Python is compiled not compiled in
|
- the function does nothing is Python is not compiled in debug mode
|
||||||
debug mode
|
|
||||||
|
|
||||||
* The following memory allocators always returns *NULL* if size is
|
* Memory allocators always returns *NULL* if size is greater than
|
||||||
greater than ``PY_SSIZE_T_MAX`` (check before calling the internal
|
``PY_SSIZE_T_MAX``. The check is done before calling the
|
||||||
function): ``PyMem_RawMalloc()``, ``PyMem_RawRealloc()``,
|
inner function.
|
||||||
``PyMem_Malloc()``, ``PyMem_Realloc()``, ``PyObject_Malloc()``,
|
|
||||||
``PyObject_Realloc()``.
|
The *pymalloc* allocator is optimized for objects smaller than 512 bytes
|
||||||
|
with a short lifetime. It uses memory mappings with a fixed size of 256
|
||||||
|
KB called "arenas".
|
||||||
|
|
||||||
|
Default allocators:
|
||||||
|
|
||||||
|
* ``PYALLOC_PYMEM_RAW``, ``PYALLOC_PYMEM``: ``malloc()``,
|
||||||
|
``realloc()``, ``free()`` (and *ctx* is NULL); call ``malloc(1)`` when
|
||||||
|
requesting zero bytes
|
||||||
|
* ``PYALLOC_PYOBJECT``: *pymalloc* allocator which fall backs on
|
||||||
|
``PyMem_Malloc()`` for allocations larger than 512 bytes
|
||||||
|
* *pymalloc* arena allocator: ``mmap()``, ``munmap()`` (and *ctx* is
|
||||||
|
NULL), or ``malloc()`` and ``free()`` if ``mmap()`` is not available
|
||||||
|
|
||||||
The builtin Python debug hooks were introduced in Python 2.3 and
|
The builtin Python debug hooks were introduced in Python 2.3 and
|
||||||
implement the following checks:
|
implement the following checks:
|
||||||
|
@ -141,23 +144,34 @@ implement the following checks:
|
||||||
* Detect write after the end of the buffer (buffer overflow)
|
* Detect write after the end of the buffer (buffer overflow)
|
||||||
|
|
||||||
|
|
||||||
Other changes
|
Don't call malloc() directly anymore
|
||||||
-------------
|
------------------------------------
|
||||||
|
|
||||||
* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` always call ``malloc()``
|
``PyMem_Malloc()`` and ``PyMem_Realloc()`` always call ``malloc()`` and
|
||||||
and ``realloc()``, instead of calling ``PyObject_Malloc()`` and
|
``realloc()``, instead of calling ``PyObject_Malloc()`` and
|
||||||
``PyObject_Realloc()`` in debug mode
|
``PyObject_Realloc()`` in debug mode.
|
||||||
|
|
||||||
* ``PyObject_Malloc()`` falls back on ``PyMem_Malloc()`` instead of
|
``PyObject_Malloc()`` falls back on ``PyMem_Malloc()`` instead of
|
||||||
``malloc()`` if size is greater or equal than
|
``malloc()`` if size is greater or equal than 512 bytes, and
|
||||||
``SMALL_REQUEST_THRESHOLD`` (512 bytes), and ``PyObject_Realloc()``
|
``PyObject_Realloc()`` falls back on ``PyMem_Realloc()`` instead of
|
||||||
falls back on ``PyMem_Realloc()`` instead of ``realloc()``
|
``realloc()``
|
||||||
|
|
||||||
* Replace direct calls to ``malloc()`` with ``PyMem_Malloc()``, or
|
Replace direct calls to ``malloc()`` with ``PyMem_Malloc()``, or
|
||||||
``PyMem_RawMalloc()`` if the GIL is not held
|
``PyMem_RawMalloc()`` if the GIL is not held.
|
||||||
|
|
||||||
* Configure external libraries like zlib or OpenSSL to allocate memory
|
Configure external libraries like zlib or OpenSSL to allocate memory
|
||||||
using ``PyMem_RawMalloc()``
|
using ``PyMem_Malloc()`` or ``PyMem_RawMalloc()``. If the allocator of a
|
||||||
|
library can only be replaced globally, the allocator is not replaced if
|
||||||
|
Python is embedded in an application.
|
||||||
|
|
||||||
|
For the "track memory usage" use case, it is important to track memory
|
||||||
|
allocated in external libraries to have accurate reports, because these
|
||||||
|
allocations may be large.
|
||||||
|
|
||||||
|
If an hook is used to the track memory usage, the memory allocated by
|
||||||
|
``malloc()`` will not be tracked. Remaining ``malloc()`` in external
|
||||||
|
libraries like OpenSSL or bz2 may allocate large memory blocks and so
|
||||||
|
would be missed in memory usage reports.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -171,8 +185,8 @@ and 10 bytes per memory mapping::
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int block_padding = 2;
|
int alloc_padding = 2;
|
||||||
int mapping_padding = 10;
|
int arena_padding = 10;
|
||||||
|
|
||||||
void* my_malloc(void *ctx, size_t size)
|
void* my_malloc(void *ctx, size_t size)
|
||||||
{
|
{
|
||||||
|
@ -191,49 +205,49 @@ and 10 bytes per memory mapping::
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* my_alloc_mapping(void *ctx, size_t size)
|
void* my_alloc_arena(void *ctx, size_t size)
|
||||||
{
|
{
|
||||||
int padding = *(int *)ctx;
|
int padding = *(int *)ctx;
|
||||||
return malloc(size + padding);
|
return malloc(size + padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
void my_free_mapping(void *ctx, void *ptr, size_t size)
|
void my_free_arena(void *ctx, void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_custom_allocator(void)
|
void setup_custom_allocator(void)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator block;
|
PyMemAllocator alloc;
|
||||||
PyMemMappingAllocator mapping;
|
PyObjectArenaAllocator arena;
|
||||||
|
|
||||||
block.ctx = &block_padding;
|
alloc.ctx = &alloc_padding;
|
||||||
block.malloc = my_malloc;
|
alloc.malloc = my_malloc;
|
||||||
block.realloc = my_realloc;
|
alloc.realloc = my_realloc;
|
||||||
block.free = my_free;
|
alloc.free = my_free;
|
||||||
|
|
||||||
PyMem_SetRawAllocator(&block);
|
PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc);
|
||||||
PyMem_SetAllocator(&block);
|
PyMem_SetAllocator(PYALLOC_PYMEM, &alloc);
|
||||||
|
|
||||||
mapping.ctx = &mapping_padding;
|
arena.ctx = &arena_padding;
|
||||||
mapping.alloc = my_alloc_mapping;
|
arena.alloc = my_alloc_arena;
|
||||||
mapping.free = my_free_mapping;
|
arena.free = my_free_arena;
|
||||||
PyMem_SetMappingAllocator(mapping);
|
PyObject_SetArenaAllocator(&arena);
|
||||||
|
|
||||||
PyMem_SetupDebugHooks();
|
PyMem_SetupDebugHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new
|
Remove the call ``PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc)`` if
|
||||||
allocator are not thread-safe.
|
the new allocator is not thread-safe.
|
||||||
|
|
||||||
|
|
||||||
Use case 2: Replace Memory Allocator, override pymalloc
|
Use case 2: Replace Memory Allocator, override pymalloc
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
If your allocator is optimized for allocation of small objects (less
|
If your allocator is optimized for allocations of objects smaller than
|
||||||
than 512 bytes) with a short lifetime, pymalloc can be overriden
|
512 bytes with a short lifetime, pymalloc can be overriden (replace
|
||||||
(replace ``PyObject_Malloc()``).
|
``PyObject_Malloc()``).
|
||||||
|
|
||||||
Dummy example wasting 2 bytes per memory block::
|
Dummy example wasting 2 bytes per memory block::
|
||||||
|
|
||||||
|
@ -260,22 +274,22 @@ Dummy example wasting 2 bytes per memory block::
|
||||||
|
|
||||||
void setup_custom_allocator(void)
|
void setup_custom_allocator(void)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator alloc;
|
PyMemAllocator alloc;
|
||||||
alloc.ctx = &padding;
|
alloc.ctx = &padding;
|
||||||
alloc.malloc = my_malloc;
|
alloc.malloc = my_malloc;
|
||||||
alloc.realloc = my_realloc;
|
alloc.realloc = my_realloc;
|
||||||
alloc.free = my_free;
|
alloc.free = my_free;
|
||||||
|
|
||||||
PyMem_SetRawAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc);
|
||||||
PyMem_SetAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYMEM, &alloc);
|
||||||
PyObject_SetAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYOBJECT, &alloc);
|
||||||
|
|
||||||
PyMem_SetupDebugHooks();
|
PyMem_SetupDebugHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new
|
Remove the call ``PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc)`` if
|
||||||
allocator are not thread-safe.
|
the new allocator is not thread-safe.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,15 +299,15 @@ Use case 3: Setup Allocator Hooks
|
||||||
Example to setup hooks on all memory allocators::
|
Example to setup hooks on all memory allocators::
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
PyMemBlockAllocator raw;
|
PyMemAllocator raw;
|
||||||
PyMemBlockAllocator mem;
|
PyMemAllocator mem;
|
||||||
PyMemBlockAllocator obj;
|
PyMemAllocator obj;
|
||||||
/* ... */
|
/* ... */
|
||||||
} hook;
|
} hook;
|
||||||
|
|
||||||
static void* hook_malloc(void *ctx, size_t size)
|
static void* hook_malloc(void *ctx, size_t size)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator *alloc = (PyMemBlockAllocator *)ctx;
|
PyMemAllocator *alloc = (PyMemAllocator *)ctx;
|
||||||
/* ... */
|
/* ... */
|
||||||
ptr = alloc->malloc(alloc->ctx, size);
|
ptr = alloc->malloc(alloc->ctx, size);
|
||||||
/* ... */
|
/* ... */
|
||||||
|
@ -302,7 +316,7 @@ Example to setup hooks on all memory allocators::
|
||||||
|
|
||||||
static void* hook_realloc(void *ctx, void *ptr, size_t new_size)
|
static void* hook_realloc(void *ctx, void *ptr, size_t new_size)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator *alloc = (PyMemBlockAllocator *)ctx;
|
PyMemAllocator *alloc = (PyMemAllocator *)ctx;
|
||||||
void *ptr2;
|
void *ptr2;
|
||||||
/* ... */
|
/* ... */
|
||||||
ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
|
ptr2 = alloc->realloc(alloc->ctx, ptr, new_size);
|
||||||
|
@ -312,7 +326,7 @@ Example to setup hooks on all memory allocators::
|
||||||
|
|
||||||
static void hook_free(void *ctx, void *ptr)
|
static void hook_free(void *ctx, void *ptr)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator *alloc = (PyMemBlockAllocator *)ctx;
|
PyMemAllocator *alloc = (PyMemAllocator *)ctx;
|
||||||
/* ... */
|
/* ... */
|
||||||
alloc->free(alloc->ctx, ptr);
|
alloc->free(alloc->ctx, ptr);
|
||||||
/* ... */
|
/* ... */
|
||||||
|
@ -320,7 +334,7 @@ Example to setup hooks on all memory allocators::
|
||||||
|
|
||||||
void setup_hooks(void)
|
void setup_hooks(void)
|
||||||
{
|
{
|
||||||
PyMemBlockAllocator alloc;
|
PyMemAllocator alloc;
|
||||||
static int installed = 0;
|
static int installed = 0;
|
||||||
|
|
||||||
if (installed)
|
if (installed)
|
||||||
|
@ -330,27 +344,28 @@ Example to setup hooks on all memory allocators::
|
||||||
alloc.malloc = hook_malloc;
|
alloc.malloc = hook_malloc;
|
||||||
alloc.realloc = hook_realloc;
|
alloc.realloc = hook_realloc;
|
||||||
alloc.free = hook_free;
|
alloc.free = hook_free;
|
||||||
|
PyMem_GetAllocator(PYALLOC_PYMEM_RAW, &hook.raw);
|
||||||
|
PyMem_GetAllocator(PYALLOC_PYMEM, &hook.mem);
|
||||||
|
PyMem_GetAllocator(PYALLOC_PYOBJECT, &hook.obj);
|
||||||
|
|
||||||
PyMem_GetRawAllocator(&hook.raw);
|
|
||||||
alloc.ctx = &hook.raw;
|
alloc.ctx = &hook.raw;
|
||||||
PyMem_SetRawAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc);
|
||||||
|
|
||||||
PyMem_GetAllocator(&hook.mem);
|
|
||||||
alloc.ctx = &hook.mem;
|
alloc.ctx = &hook.mem;
|
||||||
PyMem_SetAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYMEM, &alloc);
|
||||||
|
|
||||||
PyObject_GetAllocator(&hook.obj);
|
|
||||||
alloc.ctx = &hook.obj;
|
alloc.ctx = &hook.obj;
|
||||||
PyObject_SetAllocator(&alloc);
|
PyMem_SetAllocator(PYALLOC_PYOBJECT, &alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if hooks are not
|
Remove the call ``PyMem_SetAllocator(PYALLOC_PYMEM_RAW, &alloc)`` if
|
||||||
thread-safe.
|
hooks are not thread-safe.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
``PyMem_SetupDebugHooks()`` does not need to be called: Python debug
|
``PyMem_SetupDebugHooks()`` does not need to be called because the
|
||||||
hooks are installed automatically at startup.
|
allocator is not replaced: Python debug hooks are installed
|
||||||
|
automatically at startup.
|
||||||
|
|
||||||
|
|
||||||
Performances
|
Performances
|
||||||
|
@ -369,32 +384,22 @@ The full reports are attached to the issue #3329.
|
||||||
Alternatives
|
Alternatives
|
||||||
============
|
============
|
||||||
|
|
||||||
Only one get/set function for block allocators
|
More specific functions to get/set memory allocators
|
||||||
----------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
Replace the 6 functions:
|
Replace the 2 functions:
|
||||||
|
|
||||||
* ``void PyMem_GetRawAllocator(PyMemBlockAllocator *allocator)``
|
* ``void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)``
|
||||||
* ``void PyMem_GetAllocator(PyMemBlockAllocator *allocator)``
|
* ``void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocator *allocator)``
|
||||||
* ``void PyObject_GetAllocator(PyMemBlockAllocator *allocator)``
|
|
||||||
* ``void PyMem_SetRawAllocator(PyMemBlockAllocator *allocator)``
|
|
||||||
* ``void PyMem_SetAllocator(PyMemBlockAllocator *allocator)``
|
|
||||||
* ``void PyObject_SetAllocator(PyMemBlockAllocator *allocator)``
|
|
||||||
|
|
||||||
with 2 functions with an additional *domain* argument:
|
with:
|
||||||
|
|
||||||
* ``int PyMem_GetBlockAllocator(int domain, PyMemBlockAllocator *allocator)``
|
* ``void PyMem_GetRawAllocator(PyMemAllocator *allocator)``
|
||||||
* ``int PyMem_SetBlockAllocator(int domain, PyMemBlockAllocator *allocator)``
|
* ``void PyMem_GetAllocator(PyMemAllocator *allocator)``
|
||||||
|
* ``void PyObject_GetAllocator(PyMemAllocator *allocator)``
|
||||||
These functions return 0 on success, or -1 if the domain is unknown.
|
* ``void PyMem_SetRawAllocator(PyMemAllocator *allocator)``
|
||||||
|
* ``void PyMem_SetAllocator(PyMemAllocator *allocator)``
|
||||||
where domain is one of these values:
|
* ``void PyObject_SetAllocator(PyMemAllocator *allocator)``
|
||||||
|
|
||||||
* ``PYALLOC_PYMEM``
|
|
||||||
* ``PYALLOC_PYMEM_RAW``
|
|
||||||
* ``PYALLOC_PYOBJECT``
|
|
||||||
|
|
||||||
Drawback: the caller has to check if the result is 0, or handle the error.
|
|
||||||
|
|
||||||
|
|
||||||
Make PyMem_Malloc() reuse PyMem_RawMalloc() by default
|
Make PyMem_Malloc() reuse PyMem_RawMalloc() by default
|
||||||
|
@ -404,12 +409,6 @@ Make PyMem_Malloc() reuse PyMem_RawMalloc() by default
|
||||||
calling ``PyMem_SetRawAllocator()`` would also also patch
|
calling ``PyMem_SetRawAllocator()`` would also also patch
|
||||||
``PyMem_Malloc()`` indirectly.
|
``PyMem_Malloc()`` indirectly.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
In the implementation of this PEP (issue #3329),
|
|
||||||
``PyMem_RawMalloc(0)`` calls ``malloc(0)``,
|
|
||||||
whereas ``PyMem_Malloc(0)`` calls ``malloc(1)``.
|
|
||||||
|
|
||||||
|
|
||||||
Add a new PYDEBUGMALLOC environment variable
|
Add a new PYDEBUGMALLOC environment variable
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
@ -445,7 +444,7 @@ Define allocator functions as macros using ``__FILE__`` and ``__LINE__``
|
||||||
to get the C filename and line number of a memory allocation.
|
to get the C filename and line number of a memory allocation.
|
||||||
|
|
||||||
Example of ``PyMem_Malloc`` macro with the modified
|
Example of ``PyMem_Malloc`` macro with the modified
|
||||||
``PyMemBlockAllocator`` structure::
|
``PyMemAllocator`` structure::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument
|
/* user context passed as the first argument
|
||||||
|
@ -463,7 +462,7 @@ Example of ``PyMem_Malloc`` macro with the modified
|
||||||
/* release a memory block */
|
/* release a memory block */
|
||||||
void (*free) (void *ctx, const char *filename, int lineno,
|
void (*free) (void *ctx, const char *filename, int lineno,
|
||||||
void *ptr);
|
void *ptr);
|
||||||
} PyMemBlockAllocator;
|
} PyMemAllocator;
|
||||||
|
|
||||||
void* _PyMem_MallocTrace(const char *filename, int lineno,
|
void* _PyMem_MallocTrace(const char *filename, int lineno,
|
||||||
size_t size);
|
size_t size);
|
||||||
|
@ -485,12 +484,12 @@ changes add too much complexity for a little gain.
|
||||||
GIL-free PyMem_Malloc()
|
GIL-free PyMem_Malloc()
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
When Python is compiled in debug mode, ``PyMem_Malloc()`` calls
|
In Python 3.3, when Python is compiled in debug mode, ``PyMem_Malloc()``
|
||||||
indirectly ``PyObject_Malloc()`` which requires the GIL to be held.
|
calls indirectly ``PyObject_Malloc()`` which requires the GIL to be
|
||||||
That's why ``PyMem_Malloc()`` must be called with the GIL held.
|
held. That's why ``PyMem_Malloc()`` must be called with the GIL held.
|
||||||
|
|
||||||
This PEP proposes to "fix" ``PyMem_Malloc()`` to make it always call
|
This PEP proposes changes ``PyMem_Malloc()``: it now always call
|
||||||
``malloc()``. So the "GIL must be held" restriction may be removed from
|
``malloc()``. The "GIL must be held" restriction can be removed from
|
||||||
``PyMem_Malloc()``.
|
``PyMem_Malloc()``.
|
||||||
|
|
||||||
Allowing to call ``PyMem_Malloc()`` without holding the GIL might break
|
Allowing to call ``PyMem_Malloc()`` without holding the GIL might break
|
||||||
|
@ -516,9 +515,10 @@ Python call ``PyMem_Malloc()`` whereas the GIL do not exist yet. In this
|
||||||
case, ``PyMem_Malloc()`` should be replaced with ``malloc()`` (or
|
case, ``PyMem_Malloc()`` should be replaced with ``malloc()`` (or
|
||||||
``PyMem_RawMalloc()``).
|
``PyMem_RawMalloc()``).
|
||||||
|
|
||||||
If an hook is used to the track memory usage, the ``malloc()`` memory
|
If an hook is used to the track memory usage, the memory allocated by
|
||||||
will not be seen. Remaining ``malloc()`` may allocate a lot of memory
|
direct calls to ``malloc()`` will not be tracked. External libraries
|
||||||
and so would be missed in reports.
|
like OpenSSL or bz2 should not call ``malloc()`` directly, so large
|
||||||
|
allocated will be included in memory usage reports.
|
||||||
|
|
||||||
|
|
||||||
Use existing debug tools to analyze the memory
|
Use existing debug tools to analyze the memory
|
||||||
|
@ -545,8 +545,7 @@ GIL held allow to collect a lot of useful data from Python internals.
|
||||||
Add msize()
|
Add msize()
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Add another field to ``PyMemBlockAllocator`` and
|
Add another field to ``PyMemAllocator`` and ``PyObjectArenaAllocator``::
|
||||||
``PyMemMappingAllocator``::
|
|
||||||
|
|
||||||
size_t msize(void *ptr);
|
size_t msize(void *ptr);
|
||||||
|
|
||||||
|
@ -574,6 +573,8 @@ It is likely for an allocator hook to be reused for
|
||||||
depending on the allocator. The context is a convenient way to reuse the
|
depending on the allocator. The context is a convenient way to reuse the
|
||||||
same custom allocator or hook for different Python allocators.
|
same custom allocator or hook for different Python allocators.
|
||||||
|
|
||||||
|
In C++, the context can be used to pass *this*.
|
||||||
|
|
||||||
|
|
||||||
External libraries
|
External libraries
|
||||||
==================
|
==================
|
||||||
|
@ -589,6 +590,15 @@ Libraries used by Python:
|
||||||
* expat: `parserCreate()
|
* expat: `parserCreate()
|
||||||
<http://hg.python.org/cpython/file/cc27d50bd91a/Modules/expat/xmlparse.c#l724>`_
|
<http://hg.python.org/cpython/file/cc27d50bd91a/Modules/expat/xmlparse.c#l724>`_
|
||||||
has a per-instance memory handler
|
has a per-instance memory handler
|
||||||
|
* zlib: `zlib 1.2.8 Manual <http://www.zlib.net/manual.html#Usage>`_,
|
||||||
|
pass an opaque pointer
|
||||||
|
* bz2: `bzip2 and libbzip2, version 1.0.5
|
||||||
|
<http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html>`_,
|
||||||
|
pass an opaque pointer
|
||||||
|
* lzma: `LZMA SDK - How to Use
|
||||||
|
<http://www.asawicki.info/news_1368_lzma_sdk_-_how_to_use.html>`_,
|
||||||
|
pass an opaque pointer
|
||||||
|
* lipmpdec doesn't have this extra *ctx* parameter
|
||||||
|
|
||||||
Other libraries:
|
Other libraries:
|
||||||
|
|
||||||
|
@ -596,6 +606,9 @@ Other libraries:
|
||||||
<http://developer.gnome.org/glib/unstable/glib-Memory-Allocation.html#g-mem-set-vtable>`_
|
<http://developer.gnome.org/glib/unstable/glib-Memory-Allocation.html#g-mem-set-vtable>`_
|
||||||
* libxml2: `xmlGcMemSetup() <http://xmlsoft.org/html/libxml-xmlmemory.html>`_,
|
* libxml2: `xmlGcMemSetup() <http://xmlsoft.org/html/libxml-xmlmemory.html>`_,
|
||||||
global
|
global
|
||||||
|
* Oracle's OCI: `Oracle Call Interface Programmer's Guide,
|
||||||
|
Release 2 (9.2)
|
||||||
|
<http://docs.oracle.com/cd/B10501_01/appdev.920/a96584/oci15re4.htm>`_
|
||||||
|
|
||||||
See also the `GNU libc: Memory Allocation Hooks
|
See also the `GNU libc: Memory Allocation Hooks
|
||||||
<http://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html>`_.
|
<http://www.gnu.org/software/libc/manual/html_node/Hooks-for-Malloc.html>`_.
|
||||||
|
|
Loading…
Reference in New Issue