From 0a2d0e83a2a6c88ed2c25b4ded2c65e567e8624e Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Sat, 3 Aug 2013 01:04:04 -0700 Subject: [PATCH] Add memory allocation from wiki. --- docs/src/tutorial/index.rst | 1 + docs/src/tutorial/memory_allocation.rst | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 docs/src/tutorial/memory_allocation.rst diff --git a/docs/src/tutorial/index.rst b/docs/src/tutorial/index.rst index 57420d0..4d683c6 100644 --- a/docs/src/tutorial/index.rst +++ b/docs/src/tutorial/index.rst @@ -12,6 +12,7 @@ Tutorials caveats profiling_tutorial strings + memory_allocation pure numpy readings diff --git a/docs/src/tutorial/memory_allocation.rst b/docs/src/tutorial/memory_allocation.rst new file mode 100644 index 0000000..94ed130 --- /dev/null +++ b/docs/src/tutorial/memory_allocation.rst @@ -0,0 +1,78 @@ +.. highlight:: cython + +.. _memory_allocation: + +***************** +Memory Allocation +***************** + +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. + +Objects can be used in Cython as well, but sometimes this incurs a certain +amount of overhead. In C, simple values and structs +(such as a local variable ``cdef double x``) are allocated on the stack and +passed by value, but for larger more complicated objects +(e.g. a dynamically-sized list of doubles) memory must be +manually requested and released. +C provides the functions ``malloc``, ``realloc``, and ``free`` for this purpose, +which can be imported in cython from ``clibc.stdlib``. Their signatures are:: + + void* malloc(size_t size) + void* realloc(void* ptr, size_t size) + void free(void* ptr) + +A very simple example of malloc usage is the following:: + + import random + 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 = malloc(number * sizeof(double)) + if not my_array: + raise MemoryError() + + try: + ran = random.normalvariate + for i in range(number): + my_array[i] = ran(0,1) + + return [ my_array[i] for i in range(number) ] + finally: + # return the previously allocated memory to the system + free(my_array) + +One important thing to remember is that blocks of memory obtained with malloc +*must* be manually released with free when one is done with them or it won't +be reclaimed until the python process exits. This is called a memory leak. +If a chuck of memory needs a larger lifetime then can be managed by a +``try..finally`` block, another helpful idiom is to tie its lifetime to a +Python object to leverage the Python runtime's memory management, e.g.:: + + cdef class SomeMemory: + + cdef doube* data + + def __init__(self, number): + # allocate some memory (filled with random data) + self.data = malloc(number * sizeof(double)) + if self.data == NULL: + raise MemoryError() + + def resize(self, new_number): + # Allocates new_number * sizeof(double) bytes, + # preserving the contents and making a best-effort to + # re-use the original data location. + self.data = realloc(self.data, new_number * sizeof(double)) + + def __dealloc__(self, number): + if self.data != NULL: + free(self.data) + +It should be noted that Cython has special support for (multi-dimensional) +arrays of simple types via NumPy and memory views which are more full featured +and easier to work with than pointers while still retaining the speed/static +typing benefits. \ No newline at end of file -- 2.7.4