Skip to content

Latest commit

 

History

History
77 lines (62 loc) · 4.53 KB

File metadata and controls

77 lines (62 loc) · 4.53 KB

内存分配

动态内存分配在Python中基本上不是问题。每个东西都是一个对象,引用计数系统和垃圾收集器在不再使用时自动将内存返回给系统。

当涉及到更底层的数据缓冲区时,Cython通过NumPy、内存视图或Python的stdlib数组类型对简单类型的(多维)数组提供了特殊的支持。它们功能齐全,支持垃圾回收,并且比C语言中的纯指针更容易使用,同时仍然保持了速度和静态类型的优点。参见使用Python数组类型化内存视图

然而,在某些情况下,这些对象仍然会产生不可接受的开销,这就需要在C语言中进行手动内存管理。

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

void* malloc(size_t size)
void* realloc(void* ptr, size_t size)
void free(void* ptr)

下面是使用malloc的一个非常简单的例子:

import random
from libc.stdlib cimport malloc, free

def random_noise(int number=1):
    cdef int i
    # 分配 number * sizeof(double) 字节的内存
    cdef double *my_array = <double *> 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)

        # ... 让我们假设我们在这里做了一些更复杂的C计算,
        # 以弥补在丢弃上面的现有对象之后将下面的C double值
        # 打包到Python float对象中所做的工作。

        return [x for x in my_array[:number]]
    finally:
        # 返回预先分配的内存到系统
        free(my_array)

注意,用于在Python堆上分配内存的C-API函数通常比上面的底层C函数更受欢迎,因为它们提供的内存实际上是在Python的内部内存管理系统中计算的。它们还对较小的内存块进行了特殊的优化,从而避免了昂贵的操作系统调用,加快了内存块的分配。

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

from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free

它们的接口和用法与相对应的底层C函数相同。

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

如果一个内存块需要比管理它的try..finally块更长的生命周期,另一个有用的习惯用法是将它的生命周期绑定到Python对象上,以利用Python运行时的内存管理,例如: If a chunk of memory needs a larger lifetime than 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.:

from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free

cdef class SomeMemory:

    cdef double* data

    def __cinit__(self, size_t number):
        # 分配一些内存(不初始化,可能含有任意值)
        self.data = <double*> PyMem_Malloc(number * sizeof(double))
        if not self.data:
            raise MemoryError()

    def resize(self, size_t new_number):
        # 分配 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.
        # 若出现错误(mem is NULL),原始的内存并未被释放。
        self.data = mem

    def __dealloc__(self):
        PyMem_Free(self.data)  # 若self.data为NULL则无操作