内存分配

原文: http://docs.cython.org/en/latest/src/tutorial/memory_allocation.html

动态内存分配在 Python 中大多不是问题。一切都是对象,引用计数系统和垃圾收集器在不再使用时自动将内存返回给系统。

当谈到更多低级数据缓冲区时,Cython 通过 NumPy,内存视图或 Python 的 stdlib 数组类型特别支持简单类型的(多维)数组。它们是全功能的,垃圾收集的,比 C 中的裸指针更容易使用,同时仍然保持速度和静态类型的好处。参见 使用 Python 数组类型记忆视图

但是,在某些情况下,这些对象仍然会产生不可接受的开销,这可能会导致在 C 中进行手动内存管理。

简单的 C 值和结构(例如局部变量cdef double x)通常在堆栈上分配并通过值传递,但对于更大和更复杂的对象(例如动态大小的双精度列表),必须手动请求内存并释放。 C 为此提供函数malloc()realloc()free(),可以从clibc.stdlib导入到 cython 中。他们的签名是:

  1. void* malloc(size_t size)
  2. void* realloc(void* ptr, size_t size)
  3. void free(void* ptr)

malloc 使用的一个非常简单的示例如下:

|

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23

|

  1. import random
  2. from libc.stdlib cimport malloc, free
  3. def random_noise(int number=1):
  4. cdef int i
  5. # allocate number * sizeof(double) bytes of memory
  6. cdef double *my_array = <double *> malloc(number * sizeof(double))
  7. if not my_array:
  8. raise MemoryError()
  9. try:
  10. ran = random.normalvariate
  11. for i in range(number):
  12. my_array[i] = ran(0, 1)
  13. # ... let's just assume we do some more heavy C calculations here to make up
  14. # for the work that it takes to pack the C double values into Python float
  15. # objects below, right after throwing away the existing objects above.
  16. return [x for x in my_array[:number]]
  17. finally:
  18. # return the previously allocated memory to the system
  19. free(my_array)

|

请注意,用于在 Python 堆上分配内存的 C-API 函数通常比上面的低级 C 函数更受欢迎,因为它们提供的内存实际上是在 Python 的内部内存管理系统中考虑的。它们还对较小的内存块进行了特殊优化,通过避免代价高昂的操作系统调用来加速其分配。

可以在cpython.mem标准声明文件中找到 C-API 函数:

  1. from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free

它们的接口和用法与相应的低级 C 函数相同。

需要记住的一件重要事情是,malloc()PyMem_Malloc() 获得的内存块必须手动释放 ,并相应调用free()PyMem_Free() 当它们不再使用时( 必须 总是使用匹配类型的自由函数)。否则,在 python 进程退出之前,它们不会被回收。这称为内存泄漏。

如果一块内存需要比try..finally块可以管理的更长的生命周期,另一个有用的习惯是将其生命周期与 Python 对象联系起来以利用 Python 运行时的内存管理,例如:

  1. from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
  2. cdef class SomeMemory:
  3. cdef double* data
  4. def __cinit__(self, size_t number):
  5. # allocate some memory (uninitialised, may contain arbitrary data)
  6. self.data = <double*> PyMem_Malloc(number * sizeof(double))
  7. if not self.data:
  8. raise MemoryError()
  9. def resize(self, size_t new_number):
  10. # Allocates new_number * sizeof(double) bytes,
  11. # preserving the current content and making a best-effort to
  12. # re-use the original data location.
  13. mem = <double*> PyMem_Realloc(self.data, new_number * sizeof(double))
  14. if not mem:
  15. raise MemoryError()
  16. # Only overwrite the pointer if the memory was really reallocated.
  17. # On error (mem is NULL), the originally memory has not been freed.
  18. self.data = mem
  19. def __dealloc__(self):
  20. PyMem_Free(self.data) # no-op if self.data is NULL