PEP 445: textwidth=72 (for email)
This commit is contained in:
parent
2d81cffb61
commit
d1c9cb312b
308
pep-0445.txt
308
pep-0445.txt
|
@ -20,19 +20,21 @@ Rationale
|
||||||
|
|
||||||
Use cases:
|
Use cases:
|
||||||
|
|
||||||
* Application embedding Python may want to isolate Python memory from the
|
* Application embedding Python may want to isolate Python memory from
|
||||||
memory of the application, or may want to different memory allocator
|
the memory of the application, or may want to different memory
|
||||||
optimized for its Python usage
|
allocator optimized for its Python usage
|
||||||
* Python running on embedded devices with low memory and slow CPU.
|
* Python running on embedded devices with low memory and slow CPU.
|
||||||
A custom memory allocator may be required to use efficiently the memory
|
A custom memory allocator may be required to use efficiently the
|
||||||
and/or to be able to use all the memory of the device.
|
memory and/or to be able to use all the memory of the device.
|
||||||
* Debug tool to:
|
* Debug tool to:
|
||||||
|
|
||||||
- track memory usage (memory leaks)
|
- track memory usage (memory leaks)
|
||||||
- get the Python filename and line number where an object was allocated
|
- get the Python filename and line number where an object was
|
||||||
|
allocated
|
||||||
- detect buffer underflow, buffer overflow and detect misuse of Python
|
- detect buffer underflow, buffer overflow and detect misuse of Python
|
||||||
allocator APIs (builtin Python debug hooks)
|
allocator APIs (builtin Python debug hooks)
|
||||||
- force allocation to fail to test handling of ``MemoryError`` exception
|
- force allocation to fail to test handling of ``MemoryError``
|
||||||
|
exception
|
||||||
|
|
||||||
|
|
||||||
Proposal
|
Proposal
|
||||||
|
@ -46,13 +48,14 @@ API changes
|
||||||
- ``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* or a
|
- the behaviour of requesting zero bytes is not defined: return *NULL*
|
||||||
distinct non-*NULL* pointer depending on the platform.
|
or a distinct non-*NULL* pointer depending on the platform.
|
||||||
|
|
||||||
* Add a new ``PyMemBlockAllocator`` structure::
|
* Add a new ``PyMemBlockAllocator`` structure::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument to the 3 functions */
|
/* user context passed as the first argument
|
||||||
|
to the 3 functions */
|
||||||
void *ctx;
|
void *ctx;
|
||||||
|
|
||||||
/* allocate a memory block */
|
/* allocate a memory block */
|
||||||
|
@ -65,32 +68,34 @@ API changes
|
||||||
void (*free) (void *ctx, void *ptr);
|
void (*free) (void *ctx, void *ptr);
|
||||||
} PyMemBlockAllocator;
|
} PyMemBlockAllocator;
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of ``PyMem_RawMalloc()``,
|
* Add new functions to get and set internal functions of
|
||||||
``PyMem_RawRealloc()`` and ``PyMem_RawFree()``:
|
``PyMem_RawMalloc()``, ``PyMem_RawRealloc()`` and ``PyMem_RawFree()``:
|
||||||
|
|
||||||
- ``void PyMem_GetRawAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_GetRawAllocator(PyMemBlockAllocator *allocator)``
|
||||||
- ``void PyMem_SetRawAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_SetRawAllocator(PyMemBlockAllocator *allocator)``
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of ``PyMem_Malloc()``,
|
* Add new functions to get and set internal functions of
|
||||||
``PyMem_Realloc()`` and ``PyMem_Free()``:
|
``PyMem_Malloc()``, ``PyMem_Realloc()`` and ``PyMem_Free()``:
|
||||||
|
|
||||||
- ``void PyMem_GetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_GetAllocator(PyMemBlockAllocator *allocator)``
|
||||||
- ``void PyMem_SetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyMem_SetAllocator(PyMemBlockAllocator *allocator)``
|
||||||
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return *NULL*:
|
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return
|
||||||
it would be treated as an error.
|
*NULL*: it would be treated as an error.
|
||||||
|
|
||||||
* Add new functions to get and set internal functions of
|
* Add new functions to get and set internal functions of
|
||||||
``PyObject_Malloc()``,, ``PyObject_Realloc()`` and ``PyObject_Free()``:
|
``PyObject_Malloc()``,, ``PyObject_Realloc()`` and
|
||||||
|
``PyObject_Free()``:
|
||||||
|
|
||||||
- ``void PyObject_GetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyObject_GetAllocator(PyMemBlockAllocator *allocator)``
|
||||||
- ``void PyObject_SetAllocator(PyMemBlockAllocator *allocator)``
|
- ``void PyObject_SetAllocator(PyMemBlockAllocator *allocator)``
|
||||||
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return *NULL*:
|
- ``malloc(ctx, 0)`` and ``realloc(ctx, ptr, 0)`` must not return
|
||||||
it would be treated as an error.
|
*NULL*: it would be treated as an error.
|
||||||
|
|
||||||
* Add a new ``PyMemMappingAllocator`` structure::
|
* Add a new ``PyMemMappingAllocator`` structure::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument to the 2 functions */
|
/* user context passed as the first argument
|
||||||
|
to the 2 functions */
|
||||||
void *ctx;
|
void *ctx;
|
||||||
|
|
||||||
/* allocate a memory mapping */
|
/* allocate a memory mapping */
|
||||||
|
@ -104,26 +109,26 @@ API changes
|
||||||
|
|
||||||
- ``void PyMem_GetMappingAllocator(PyMemMappingAllocator *allocator)``
|
- ``void PyMem_GetMappingAllocator(PyMemMappingAllocator *allocator)``
|
||||||
- ``void PyMem_SetMappingAllocator(PyMemMappingAllocator *allocator)``
|
- ``void PyMem_SetMappingAllocator(PyMemMappingAllocator *allocator)``
|
||||||
- Currently, this allocator is only used internally by *pymalloc* to allocate
|
- Currently, this allocator is only used internally by *pymalloc* to
|
||||||
arenas.
|
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 memory
|
||||||
allocators are replaced:
|
allocators are replaced:
|
||||||
|
|
||||||
- ``void PyMem_SetupDebugHooks(void)``
|
- ``void PyMem_SetupDebugHooks(void)``
|
||||||
|
|
||||||
* The following memory allocators always returns *NULL* if size is greater
|
* The following memory allocators always returns *NULL* if size is
|
||||||
than ``PY_SSIZE_T_MAX``: ``PyMem_RawMalloc()``, ``PyMem_RawRealloc()``,
|
greater than ``PY_SSIZE_T_MAX``: ``PyMem_RawMalloc()``,
|
||||||
``PyMem_Malloc()``, ``PyMem_Realloc()``, ``PyObject_Malloc()``,
|
``PyMem_RawRealloc()``, ``PyMem_Malloc()``, ``PyMem_Realloc()``,
|
||||||
``PyObject_Realloc()``.
|
``PyObject_Malloc()``, ``PyObject_Realloc()``.
|
||||||
|
|
||||||
The builtin Python debug hooks were introduced in Python 2.3 and implement the
|
The builtin Python debug hooks were introduced in Python 2.3 and
|
||||||
following checks:
|
implement the following checks:
|
||||||
|
|
||||||
* Newly allocated memory is filled with the byte ``0xCB``, freed memory is
|
* Newly allocated memory is filled with the byte ``0xCB``, freed memory
|
||||||
filled with the byte ``0xDB``.
|
is filled with the byte ``0xDB``.
|
||||||
* Detect API violations, ex: ``PyObject_Free()`` called on a memory block
|
* Detect API violations, ex: ``PyObject_Free()`` called on a memory
|
||||||
allocated by ``PyMem_Malloc()``
|
block allocated by ``PyMem_Malloc()``
|
||||||
* Detect write before the start of the buffer (buffer underflow)
|
* Detect write before the start of the buffer (buffer underflow)
|
||||||
* Detect write after the end of the buffer (buffer overflow)
|
* Detect write after the end of the buffer (buffer overflow)
|
||||||
|
|
||||||
|
@ -134,20 +139,20 @@ The *pymalloc* allocator is used by default for:
|
||||||
Make usage of these new APIs
|
Make usage of these new APIs
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` always call ``malloc()`` and
|
* ``PyMem_Malloc()`` and ``PyMem_Realloc()`` always call ``malloc()``
|
||||||
``realloc()``, instead of calling ``PyObject_Malloc()`` and
|
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 ``SMALL_REQUEST_THRESHOLD``
|
``malloc()`` if size is greater or equal than
|
||||||
(512 bytes), and ``PyObject_Realloc()`` falls back on ``PyMem_Realloc()``
|
``SMALL_REQUEST_THRESHOLD`` (512 bytes), and ``PyObject_Realloc()``
|
||||||
instead of ``realloc()``
|
falls back on ``PyMem_Realloc()`` instead of ``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 using
|
* Configure external libraries like zlib or OpenSSL to allocate memory
|
||||||
``PyMem_RawMalloc()``
|
using ``PyMem_RawMalloc()``
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
|
@ -213,16 +218,16 @@ Dummy example wasting 2 bytes per allocation, and 10 bytes per arena::
|
||||||
}
|
}
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new allocator
|
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new
|
||||||
are not thread-safe.
|
allocator are 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 than 512
|
If your allocator is optimized for allocation of small objects (less
|
||||||
bytes) with a short lifetime, pymalloc can be overriden: replace
|
than 512 bytes) with a short lifetime, pymalloc can be overriden:
|
||||||
``PyObject_Malloc()``.
|
replace ``PyObject_Malloc()``.
|
||||||
|
|
||||||
Dummy Example wasting 2 bytes per allocation::
|
Dummy Example wasting 2 bytes per allocation::
|
||||||
|
|
||||||
|
@ -263,8 +268,8 @@ Dummy Example wasting 2 bytes per allocation::
|
||||||
}
|
}
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new allocator
|
Remove the call ``PyMem_SetRawAllocator(&alloc)`` if the new
|
||||||
are not thread-safe.
|
allocator are not thread-safe.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,20 +343,21 @@ Example to setup hooks on all memory allocators::
|
||||||
thread-safe.
|
thread-safe.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
``PyMem_SetupDebugHooks()`` does not need to be called: Python debug hooks
|
``PyMem_SetupDebugHooks()`` does not need to be called: Python debug
|
||||||
are installed automatically at startup.
|
hooks are installed automatically at startup.
|
||||||
|
|
||||||
|
|
||||||
Performances
|
Performances
|
||||||
============
|
============
|
||||||
|
|
||||||
Results of the `Python benchmarks suite <http://hg.python.org/benchmarks>`_ (-b
|
Results of the `Python benchmarks suite
|
||||||
2n3): some tests are 1.04x faster, some tests are 1.04 slower, significant is
|
<http://hg.python.org/benchmarks>`_ (-b 2n3): some tests are 1.04x
|
||||||
between 115 and -191. I don't understand these output, but I guess that the
|
faster, some tests are 1.04 slower, significant is between 115 and -191.
|
||||||
overhead cannot be seen with such test.
|
I don't understand these output, but I guess that the overhead cannot be
|
||||||
|
seen with such test.
|
||||||
|
|
||||||
Results of pybench benchmark: "+0.1%" slower globally (diff between -4.9% and
|
Results of pybench benchmark: "+0.1%" slower globally (diff between
|
||||||
+5.6%).
|
-4.9% and +5.6%).
|
||||||
|
|
||||||
The full reports are attached to the issue #3329.
|
The full reports are attached to the issue #3329.
|
||||||
|
|
||||||
|
@ -390,9 +396,9 @@ Drawback: the caller has to check if the result is 0, or handle the error.
|
||||||
PyMem_Malloc() reuses PyMem_RawMalloc() by default
|
PyMem_Malloc() reuses PyMem_RawMalloc() by default
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
``PyMem_Malloc()`` should call ``PyMem_RawMalloc()`` by default. So calling
|
``PyMem_Malloc()`` should call ``PyMem_RawMalloc()`` by default. So
|
||||||
``PyMem_SetRawAllocator()`` would also also patch ``PyMem_Malloc()``
|
calling ``PyMem_SetRawAllocator()`` would also also patch
|
||||||
indirectly.
|
``PyMem_Malloc()`` indirectly.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
@ -404,39 +410,42 @@ indirectly.
|
||||||
Add a new PYDEBUGMALLOC environment variable
|
Add a new PYDEBUGMALLOC environment variable
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
To be able to use the Python builtin debug hooks even when a custom memory
|
To be able to use the Python builtin debug hooks even when a custom
|
||||||
allocator replaces the default Python allocator, an environment variable
|
memory allocator replaces the default Python allocator, an environment
|
||||||
``PYDEBUGMALLOC`` can be added to setup these debug function hooks, instead of
|
variable ``PYDEBUGMALLOC`` can be added to setup these debug function
|
||||||
adding the new function ``PyMem_SetupDebugHooks()``. If the environment
|
hooks, instead of adding the new function ``PyMem_SetupDebugHooks()``.
|
||||||
variable is present, ``PyMem_SetRawAllocator()``, ``PyMem_SetAllocator()``
|
If the environment variable is present, ``PyMem_SetRawAllocator()``,
|
||||||
and ``PyObject_SetAllocator()`` will reinstall automatically the hook on top
|
``PyMem_SetAllocator()`` and ``PyObject_SetAllocator()`` will reinstall
|
||||||
of the new allocator.
|
automatically the hook on top of the new allocator.
|
||||||
|
|
||||||
An new environment variable would make the Python initialization even more
|
An new environment variable would make the Python initialization even
|
||||||
complex. The `PEP 432 <http://www.python.org/dev/peps/pep-0432/>`_ tries to
|
more complex. The `PEP 432 <http://www.python.org/dev/peps/pep-0432/>`_
|
||||||
simply the CPython startup sequence.
|
tries to simply the CPython startup sequence.
|
||||||
|
|
||||||
|
|
||||||
Use macros to get customizable allocators
|
Use macros to get customizable allocators
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
To have no overhead in the default configuration, customizable allocators would
|
To have no overhead in the default configuration, customizable
|
||||||
be an optional feature enabled by a configuration option or by macros.
|
allocators would be an optional feature enabled by a configuration
|
||||||
|
option or by macros.
|
||||||
|
|
||||||
Not having to recompile Python makes debug hooks easier to use in practice.
|
Not having to recompile Python makes debug hooks easier to use in
|
||||||
Extensions modules don't have to be recompiled with macros.
|
practice. Extensions modules don't have to be recompiled with macros.
|
||||||
|
|
||||||
|
|
||||||
Pass the C filename and line number
|
Pass the C filename and line number
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Define allocator functions using macros and use ``__FILE__`` and ``__LINE__``
|
Define allocator functions using macros and use ``__FILE__`` and
|
||||||
to get the C filename and line number of a memory allocation.
|
``__LINE__`` to get the C filename and line number of a memory
|
||||||
|
allocation.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* user context passed as the first argument to the 3 functions */
|
/* user context passed as the first argument
|
||||||
|
to the 3 functions */
|
||||||
void *ctx;
|
void *ctx;
|
||||||
|
|
||||||
/* allocate a memory block */
|
/* allocate a memory block */
|
||||||
|
@ -452,7 +461,8 @@ Example::
|
||||||
void *ptr);
|
void *ptr);
|
||||||
} PyMemBlockAllocator;
|
} PyMemBlockAllocator;
|
||||||
|
|
||||||
void* _PyMem_MallocTrace(const char *filename, int lineno, size_t size);
|
void* _PyMem_MallocTrace(const char *filename, int lineno,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
/* need also a function for the Python stable ABI */
|
/* need also a function for the Python stable ABI */
|
||||||
void* PyMem_Malloc(size_t size);
|
void* PyMem_Malloc(size_t size);
|
||||||
|
@ -470,7 +480,8 @@ 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 indirectly ``PyObject_Malloc()`` which requires the GIL to be held.
|
When Python is compiled in debug mode, ``PyMem_Malloc()`` calls
|
||||||
|
indirectly ``PyObject_Malloc()`` which requires the GIL to be held.
|
||||||
That's why ``PyMem_Malloc()`` must be called with the GIL 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 to "fix" ``PyMem_Malloc()`` to make it always call
|
||||||
|
@ -478,64 +489,65 @@ This PEP proposes to "fix" ``PyMem_Malloc()`` to make it always call
|
||||||
``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
|
||||||
applications which setup their own allocators or allocator hooks. Holding the
|
applications which setup their own allocators or allocator hooks.
|
||||||
GIL is convinient to develop a custom allocator: no need to care of other
|
Holding the GIL is convinient to develop a custom allocator: no need to
|
||||||
threads. It is also convinient for a debug allocator hook: Python internal
|
care of other threads. It is also convinient for a debug allocator hook:
|
||||||
objects can be safetly inspected.
|
Python internal objects can be safetly inspected.
|
||||||
|
|
||||||
Calling ``PyGILState_Ensure()`` in a memory allocator may have unexpected
|
Calling ``PyGILState_Ensure()`` in a memory allocator may have
|
||||||
behaviour, especially at Python startup and at creation of a new Python thread
|
unexpected behaviour, especially at Python startup and at creation of a
|
||||||
state.
|
new Python thread state.
|
||||||
|
|
||||||
|
|
||||||
Don't add PyMem_RawMalloc()
|
Don't add PyMem_RawMalloc()
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
Replace ``malloc()`` with ``PyMem_Malloc()``, but only if the GIL is held.
|
Replace ``malloc()`` with ``PyMem_Malloc()``, but only if the GIL is
|
||||||
Otherwise, keep ``malloc()`` unchanged.
|
held. Otherwise, keep ``malloc()`` unchanged.
|
||||||
|
|
||||||
The ``PyMem_Malloc()`` is used without the GIL held in some Python functions.
|
The ``PyMem_Malloc()`` is used without the GIL held in some Python
|
||||||
For example, the ``main()`` and ``Py_Main()`` functions of Python call
|
functions. For example, the ``main()`` and ``Py_Main()`` functions of
|
||||||
``PyMem_Malloc()`` whereas the GIL do not exist yet. In this case,
|
Python call ``PyMem_Malloc()`` whereas the GIL do not exist yet. In this
|
||||||
``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 will not
|
If an hook is used to the track memory usage, the ``malloc()`` memory
|
||||||
be seen. Remaining ``malloc()`` may allocate a lot of memory and so would be
|
will not be seen. Remaining ``malloc()`` may allocate a lot of memory
|
||||||
missed in reports.
|
and so would be missed in reports.
|
||||||
|
|
||||||
|
|
||||||
Use existing debug tools to analyze the memory
|
Use existing debug tools to analyze the memory
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
There are many existing debug tools to analyze the memory. Some examples:
|
There are many existing debug tools to analyze the memory. Some
|
||||||
`Valgrind <http://valgrind.org/>`_,
|
examples: `Valgrind <http://valgrind.org/>`_, `Purify
|
||||||
`Purify <http://ibm.com/software/awdtools/purify/>`_,
|
<http://ibm.com/software/awdtools/purify/>`_, `Clang AddressSanitizer
|
||||||
`Clang AddressSanitizer <http://code.google.com/p/address-sanitizer/>`_,
|
<http://code.google.com/p/address-sanitizer/>`_, `failmalloc
|
||||||
`failmalloc <http://www.nongnu.org/failmalloc/>`_,
|
<http://www.nongnu.org/failmalloc/>`_, etc.
|
||||||
etc.
|
|
||||||
|
|
||||||
The problem is to retrieve the Python object related to a memory pointer to read
|
The problem is to retrieve the Python object related to a memory pointer
|
||||||
its type and/or content. Another issue is to retrieve the location of the
|
to read its type and/or content. Another issue is to retrieve the
|
||||||
memory allocation: the C backtrace is usually useless (same reasoning than
|
location of the memory allocation: the C backtrace is usually useless
|
||||||
macros using ``__FILE__`` and ``__LINE__``), the Python filename and line
|
(same reasoning than macros using ``__FILE__`` and ``__LINE__``), the
|
||||||
number (or even the Python traceback) is more useful.
|
Python filename and line number (or even the Python traceback) is more
|
||||||
|
useful.
|
||||||
|
|
||||||
Classic tools are unable to introspect Python internals to collect such
|
Classic tools are unable to introspect Python internals to collect such
|
||||||
information. Being able to setup a hook on allocators called with the GIL held
|
information. Being able to setup a hook on allocators called with the
|
||||||
allow to collect a lot of useful data from Python internals.
|
GIL held allow to collect a lot of useful data from Python internals.
|
||||||
|
|
||||||
|
|
||||||
Add msize()
|
Add msize()
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Add another field to ``PyMemBlockAllocator`` and ``PyMemMappingAllocator``::
|
Add another field to ``PyMemBlockAllocator`` and
|
||||||
|
``PyMemMappingAllocator``::
|
||||||
|
|
||||||
size_t msize(void *ptr);
|
size_t msize(void *ptr);
|
||||||
|
|
||||||
This function returns the size of a memory block or a memory mapping. Return
|
This function returns the size of a memory block or a memory mapping.
|
||||||
(size_t)-1 if the function is not implemented or if the pointer is unknown
|
Return (size_t)-1 if the function is not implemented or if the pointer
|
||||||
(ex: NULL pointer).
|
is unknown (ex: NULL pointer).
|
||||||
|
|
||||||
On Windows, this function can be implemented using ``_msize()`` and
|
On Windows, this function can be implemented using ``_msize()`` and
|
||||||
``VirtualQuery()``.
|
``VirtualQuery()``.
|
||||||
|
@ -544,24 +556,25 @@ On Windows, this function can be implemented using ``_msize()`` and
|
||||||
No context argument
|
No context argument
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
Simplify the signature of allocator functions, remove the context argument:
|
Simplify the signature of allocator functions, remove the context
|
||||||
|
argument:
|
||||||
|
|
||||||
* ``void* malloc(size_t size)``
|
* ``void* malloc(size_t size)``
|
||||||
* ``void* realloc(void *ptr, size_t new_size)``
|
* ``void* realloc(void *ptr, size_t new_size)``
|
||||||
* ``void free(void *ptr)``
|
* ``void free(void *ptr)``
|
||||||
|
|
||||||
It is likely for an allocator hook to be reused for ``PyMem_SetAllocator()``
|
It is likely for an allocator hook to be reused for
|
||||||
and ``PyObject_SetAllocator()``, or even ``PyMem_SetRawAllocator()``, but the
|
``PyMem_SetAllocator()`` and ``PyObject_SetAllocator()``, or even
|
||||||
hook must call a different function depending on the allocator. The context is
|
``PyMem_SetRawAllocator()``, but the hook must call a different function
|
||||||
a convenient way to reuse the same custom allocator or hook for different
|
depending on the allocator. The context is a convenient way to reuse the
|
||||||
Python allocators.
|
same custom allocator or hook for different Python allocators.
|
||||||
|
|
||||||
|
|
||||||
External libraries
|
External libraries
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Python should try to reuse the same prototypes for allocator functions than
|
Python should try to reuse the same prototypes for allocator functions
|
||||||
other libraries.
|
than other libraries.
|
||||||
|
|
||||||
Libraries used by Python:
|
Libraries used by Python:
|
||||||
|
|
||||||
|
@ -586,36 +599,41 @@ See also the `GNU libc: Memory Allocation Hooks
|
||||||
Memory allocators
|
Memory allocators
|
||||||
=================
|
=================
|
||||||
|
|
||||||
The C standard library provides the well known ``malloc()`` function. Its
|
The C standard library provides the well known ``malloc()`` function.
|
||||||
implementation depends on the platform and of the C library. The GNU C library
|
Its implementation depends on the platform and of the C library. The GNU
|
||||||
uses a modified ptmalloc2, based on "Doug Lea's Malloc" (dlmalloc). FreeBSD
|
C library uses a modified ptmalloc2, based on "Doug Lea's Malloc"
|
||||||
uses `jemalloc <http://www.canonware.com/jemalloc/>`_. Google provides
|
(dlmalloc). FreeBSD uses `jemalloc
|
||||||
tcmalloc which is part of `gperftools <http://code.google.com/p/gperftools/>`_.
|
<http://www.canonware.com/jemalloc/>`_. Google provides tcmalloc which
|
||||||
|
is part of `gperftools <http://code.google.com/p/gperftools/>`_.
|
||||||
|
|
||||||
``malloc()`` uses two kinds of memory: heap and memory mappings. Memory
|
``malloc()`` uses two kinds of memory: heap and memory mappings. Memory
|
||||||
mappings are usually used for large allocations (ex: larger than 256 KB),
|
mappings are usually used for large allocations (ex: larger than 256
|
||||||
whereas the heap is used for small allocations.
|
KB), whereas the heap is used for small allocations.
|
||||||
|
|
||||||
On UNIX, the heap is handled by ``brk()`` and ``sbrk()`` system calls on Linux,
|
On UNIX, the heap is handled by ``brk()`` and ``sbrk()`` system calls on
|
||||||
and it is contiguous. On Windows, the heap is handled by ``HeapAlloc()`` and
|
Linux, and it is contiguous. On Windows, the heap is handled by
|
||||||
may be discontiguous. Memory mappings are handled by ``mmap()`` on UNIX and
|
``HeapAlloc()`` and may be discontiguous. Memory mappings are handled by
|
||||||
``VirtualAlloc()`` on Windows, they may be discontiguous.
|
``mmap()`` on UNIX and ``VirtualAlloc()`` on Windows, they may be
|
||||||
|
discontiguous.
|
||||||
|
|
||||||
Releasing a memory mapping gives back immediatly the memory to the system. On
|
Releasing a memory mapping gives back immediatly the memory to the
|
||||||
UNIX, heap memory is only given back to the system if it is at the end of the
|
system. On UNIX, heap memory is only given back to the system if it is
|
||||||
heap. Otherwise, the memory will only be given back to the system when all the
|
at the end of the heap. Otherwise, the memory will only be given back to
|
||||||
memory located after the released memory are also released.
|
the system when all the memory located after the released memory are
|
||||||
|
also released.
|
||||||
|
|
||||||
To allocate memory in the heap, the allocator tries to reuse free space. If
|
To allocate memory in the heap, the allocator tries to reuse free space.
|
||||||
there is no contiguous space big enough, the heap must be increased, even if we
|
If there is no contiguous space big enough, the heap must be increased,
|
||||||
have more free space than required size. This issue is called the "memory
|
even if we have more free space than required size. This issue is
|
||||||
fragmentation": the memory usage seen by the system may be much higher than
|
called the "memory fragmentation": the memory usage seen by the system
|
||||||
real usage. On Windows, ``HeapAlloc()`` creates a new memory mapping with
|
may be much higher than real usage. On Windows, ``HeapAlloc()`` creates
|
||||||
``VirtualAlloc()`` if there is not enough free contiguous memory.
|
a new memory mapping with ``VirtualAlloc()`` if there is not enough free
|
||||||
|
contiguous memory.
|
||||||
|
|
||||||
CPython has a *pymalloc* allocator for allocations smaller than 512 bytes. This
|
CPython has a *pymalloc* allocator for allocations smaller than 512
|
||||||
allocator is optimized for small objects with a short lifetime. It uses memory
|
bytes. This allocator is optimized for small objects with a short
|
||||||
mappings called "arenas" with a fixed size of 256 KB.
|
lifetime. It uses memory mappings called "arenas" with a fixed size of
|
||||||
|
256 KB.
|
||||||
|
|
||||||
Other allocators:
|
Other allocators:
|
||||||
|
|
||||||
|
@ -641,10 +659,10 @@ CPython issues related to memory allocation:
|
||||||
<http://bugs.python.org/issue13483>`_
|
<http://bugs.python.org/issue13483>`_
|
||||||
* `Issue #16742: PyOS_Readline drops GIL and calls PyOS_StdioReadline, which
|
* `Issue #16742: PyOS_Readline drops GIL and calls PyOS_StdioReadline, which
|
||||||
isn't thread safe <http://bugs.python.org/issue16742>`_
|
isn't thread safe <http://bugs.python.org/issue16742>`_
|
||||||
* `Issue #18203: Replace calls to malloc() with PyMem_Malloc() or PyMem_RawMalloc()
|
* `Issue #18203: Replace calls to malloc() with PyMem_Malloc() or
|
||||||
<http://bugs.python.org/issue18203>`_
|
PyMem_RawMalloc() <http://bugs.python.org/issue18203>`_
|
||||||
* `Issue #18227: Use Python memory allocators in external libraries like zlib
|
* `Issue #18227: Use Python memory allocators in external libraries like
|
||||||
or OpenSSL <http://bugs.python.org/issue18227>`_
|
zlib or OpenSSL <http://bugs.python.org/issue18227>`_
|
||||||
|
|
||||||
Projects analyzing the memory usage of Python applications:
|
Projects analyzing the memory usage of Python applications:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue