summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0dminnimda <0dminnimda@gmail.com>2021-08-08 11:30:07 +0300
committerGitHub <noreply@github.com>2021-08-08 10:30:07 +0200
commit3a0ca192ed497f81c7bee60f13f635c9ac265e72 (patch)
tree681ea6e4a36299c47bd0ebe2ac40f38cda761481
parentec8c0804f1a847e7f9b92210d756507afb6ae9b3 (diff)
downloadcython-3a0ca192ed497f81c7bee60f13f635c9ac265e72.tar.gz
docs: Pythonise documentation on Memory Allocation (memory_allocation.rst) (GH-4316)
-rw-r--r--docs/examples/tutorial/memory_allocation/malloc.py24
-rw-r--r--docs/examples/tutorial/memory_allocation/malloc.pyx3
-rw-r--r--docs/examples/tutorial/memory_allocation/some_memory.py27
-rw-r--r--docs/examples/tutorial/memory_allocation/some_memory.pyx8
-rw-r--r--docs/src/tutorial/memory_allocation.rst40
5 files changed, 92 insertions, 10 deletions
diff --git a/docs/examples/tutorial/memory_allocation/malloc.py b/docs/examples/tutorial/memory_allocation/malloc.py
new file mode 100644
index 000000000..fb7e82236
--- /dev/null
+++ b/docs/examples/tutorial/memory_allocation/malloc.py
@@ -0,0 +1,24 @@
+import random
+from cython.cimports.libc.stdlib import malloc, free
+
+def random_noise(number: cython.int = 1):
+ i: cython.int
+ # allocate number * sizeof(double) bytes of memory
+ my_array: cython.p_double = cython.cast(cython.p_double, malloc(
+ number * cython.sizeof(cython.double)))
+ if not my_array:
+ raise MemoryError()
+
+ try:
+ ran = random.normalvariate
+ for i in range(number):
+ my_array[i] = ran(0, 1)
+
+ # ... let's just assume we do some more heavy C calculations here to make up
+ # for the work that it takes to pack the C double values into Python float
+ # objects below, right after throwing away the existing objects above.
+
+ return [x for x in my_array[:number]]
+ finally:
+ # return the previously allocated memory to the system
+ free(my_array)
diff --git a/docs/examples/tutorial/memory_allocation/malloc.pyx b/docs/examples/tutorial/memory_allocation/malloc.pyx
index c185187d4..6aa583aab 100644
--- a/docs/examples/tutorial/memory_allocation/malloc.pyx
+++ b/docs/examples/tutorial/memory_allocation/malloc.pyx
@@ -4,7 +4,8 @@ from libc.stdlib cimport malloc, free
def random_noise(int number=1):
cdef int i
# allocate number * sizeof(double) bytes of memory
- cdef double *my_array = <double *> malloc(number * sizeof(double))
+ cdef double *my_array = <double *> malloc(
+ number * sizeof(double))
if not my_array:
raise MemoryError()
diff --git a/docs/examples/tutorial/memory_allocation/some_memory.py b/docs/examples/tutorial/memory_allocation/some_memory.py
new file mode 100644
index 000000000..31ad63a6e
--- /dev/null
+++ b/docs/examples/tutorial/memory_allocation/some_memory.py
@@ -0,0 +1,27 @@
+from cython.cimports.cpython.mem import PyMem_Malloc, PyMem_Realloc, PyMem_Free
+
+@cython.cclass
+class SomeMemory:
+ data: cython.p_double
+
+ def __cinit__(self, number: cython.size_t):
+ # allocate some memory (uninitialised, may contain arbitrary data)
+ self.data = cython.cast(cython.p_double, PyMem_Malloc(
+ number * cython.sizeof(cython.double)))
+ if not self.data:
+ raise MemoryError()
+
+ def resize(self, new_number: cython.size_t):
+ # Allocates new_number * sizeof(double) bytes,
+ # preserving the current content and making a best-effort to
+ # re-use the original data location.
+ mem = cython.cast(cython.p_double, PyMem_Realloc(
+ self.data, new_number * cython.sizeof(cython.double)))
+ if not mem:
+ raise MemoryError()
+ # Only overwrite the pointer if the memory was really reallocated.
+ # On error (mem is NULL), the originally memory has not been freed.
+ self.data = mem
+
+ def __dealloc__(self):
+ PyMem_Free(self.data) # no-op if self.data is NULL
diff --git a/docs/examples/tutorial/memory_allocation/some_memory.pyx b/docs/examples/tutorial/memory_allocation/some_memory.pyx
index fb272a88d..e6bb63b77 100644
--- a/docs/examples/tutorial/memory_allocation/some_memory.pyx
+++ b/docs/examples/tutorial/memory_allocation/some_memory.pyx
@@ -1,12 +1,13 @@
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
-cdef class SomeMemory:
+cdef class SomeMemory:
cdef double* data
def __cinit__(self, size_t number):
# allocate some memory (uninitialised, may contain arbitrary data)
- self.data = <double*> PyMem_Malloc(number * sizeof(double))
+ self.data = <double*> PyMem_Malloc(
+ number * sizeof(double))
if not self.data:
raise MemoryError()
@@ -14,7 +15,8 @@ cdef class SomeMemory:
# Allocates new_number * sizeof(double) bytes,
# preserving the current content and making a best-effort to
# re-use the original data location.
- mem = <double*> PyMem_Realloc(self.data, new_number * sizeof(double))
+ mem = <double*> PyMem_Realloc(
+ self.data, new_number * sizeof(double))
if not mem:
raise MemoryError()
# Only overwrite the pointer if the memory was really reallocated.
diff --git a/docs/src/tutorial/memory_allocation.rst b/docs/src/tutorial/memory_allocation.rst
index a6dc0ae1b..fa79310de 100644
--- a/docs/src/tutorial/memory_allocation.rst
+++ b/docs/src/tutorial/memory_allocation.rst
@@ -4,6 +4,9 @@
Memory Allocation
*****************
+.. include::
+ ../two-syntax-variants-used
+
Dynamic memory allocation is mostly a non-issue in Python. Everything is an
object, and the reference counting system and garbage collector automatically
return memory to the system when it is no longer being used.
@@ -19,7 +22,7 @@ In some situations, however, these objects can still incur an unacceptable
amount of overhead, which can then makes a case for doing manual memory
management in C.
-Simple C values and structs (such as a local variable ``cdef double x``) are
+Simple C values and structs (such as a local variable ``cdef double x`` / ``x: cython.double``) are
usually :term:`allocated on the stack<Stack allocation>` and passed by value, but for larger and more
complicated objects (e.g. a dynamically-sized list of doubles), the memory must
be :term:`manually requested and released<Heap allocation>`. C provides the functions :c:func:`malloc`,
@@ -34,8 +37,15 @@ in cython from ``clibc.stdlib``. Their signatures are:
A very simple example of malloc usage is the following:
-.. literalinclude:: ../../examples/tutorial/memory_allocation/malloc.pyx
- :linenos:
+
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/memory_allocation/malloc.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/memory_allocation/malloc.pyx
Note that the C-API functions for allocating memory on the Python heap
are generally preferred over the low-level C functions above as the
@@ -45,9 +55,20 @@ smaller memory blocks, which speeds up their allocation by avoiding
costly operating system calls.
The C-API functions can be found in the ``cpython.mem`` standard
-declarations file::
+declarations file:
+
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. code-block:: python
- from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
+ from cython.cimports.cpython.mem import PyMem_Malloc, PyMem_Realloc, PyMem_Free
+
+ .. group-tab:: Cython
+
+ .. code-block:: cython
+
+ from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
Their interface and usage is identical to that of the corresponding
low-level C functions.
@@ -64,4 +85,11 @@ If a chunk of memory needs a larger lifetime than can be managed by a
to a Python object to leverage the Python runtime's memory management,
e.g.:
-.. literalinclude:: ../../examples/tutorial/memory_allocation/some_memory.pyx
+.. tabs::
+ .. group-tab:: Pure Python
+
+ .. literalinclude:: ../../examples/tutorial/memory_allocation/some_memory.py
+
+ .. group-tab:: Cython
+
+ .. literalinclude:: ../../examples/tutorial/memory_allocation/some_memory.pyx