跳转到主要内容

通过Cython管理calloc/free调用

项目描述

cymem: 一个Cython内存助手

cymem为Cython提供两个小的内存管理助手。它们可以轻松地将内存与Python对象的生存周期绑定,以便在对象被垃圾回收时释放内存。

tests pypi Version conda Version Python wheels

概述

最有用的是cymem.Pool,它是对calloc函数的轻量级包装

from cymem.cymem cimport Pool
cdef Pool mem = Pool()
data1 = <int*>mem.alloc(10, sizeof(int))
data2 = <float*>mem.alloc(12, sizeof(float))

《Pool》对象在内部保存内存地址,并在对象被垃圾回收时释放它们。通常,您会将《Pool》附加到某些cdef'd类。这对于深度嵌套的结构特别有用,这些结构具有复杂的初始化函数。只需将《Pool》对象传递给初始化函数,您就无需担心释放您的结构——所有对《Pool.alloc》的调用都会在《Pool》到期时自动释放。

安装

通过pip安装,并需要Cython。在安装之前,请确保您的《pip》、《setuptools》和《wheel》都是最新的。

pip install -U pip setuptools wheel
pip install cymem

示例用例:结构数组

假设我们想要一个稀疏矩阵序列。我们需要快速访问,而Python列表的性能不够好。因此,我们想要一个C数组或C++向量,这意味着稀疏矩阵必须是一个C级别的结构——它不能是一个Python类。我们可以在Cython中轻松地编写这个

"""Example without Cymem

To use an array of structs, we must carefully walk the data structure when
we deallocate it.
"""

from libc.stdlib cimport calloc, free

cdef struct SparseRow:
    size_t length
    size_t* indices
    double* values

cdef struct SparseMatrix:
    size_t length
    SparseRow* rows

cdef class MatrixArray:
    cdef size_t length
    cdef SparseMatrix** matrices

    def __cinit__(self, list py_matrices):
        self.length = 0
        self.matrices = NULL

    def __init__(self, list py_matrices):
        self.length = len(py_matrices)
        self.matrices = <SparseMatrix**>calloc(len(py_matrices), sizeof(SparseMatrix*))

        for i, py_matrix in enumerate(py_matrices):
            self.matrices[i] = sparse_matrix_init(py_matrix)

    def __dealloc__(self):
        for i in range(self.length):
            sparse_matrix_free(self.matrices[i])
        free(self.matrices)


cdef SparseMatrix* sparse_matrix_init(list py_matrix) except NULL:
    sm = <SparseMatrix*>calloc(1, sizeof(SparseMatrix))
    sm.length = len(py_matrix)
    sm.rows = <SparseRow*>calloc(sm.length, sizeof(SparseRow))
    cdef size_t i, j
    cdef dict py_row
    cdef size_t idx
    cdef double value
    for i, py_row in enumerate(py_matrix):
        sm.rows[i].length = len(py_row)
        sm.rows[i].indices = <size_t*>calloc(sm.rows[i].length, sizeof(size_t))
        sm.rows[i].values = <double*>calloc(sm.rows[i].length, sizeof(double))
        for j, (idx, value) in enumerate(py_row.items()):
            sm.rows[i].indices[j] = idx
            sm.rows[i].values[j] = value
    return sm


cdef void* sparse_matrix_free(SparseMatrix* sm) except *:
    cdef size_t i
    for i in range(sm.length):
        free(sm.rows[i].indices)
        free(sm.rows[i].values)
    free(sm.rows)
    free(sm)

我们尽可能地在一个低级别的Python ref-counted类中封装数据结构,考虑到我们的性能限制。这允许我们在Cython的特殊方法《__cinit__》和《__dealloc__》中分配和释放内存。

然而,在编写《__dealloc__》和《sparse_matrix_free》函数时,很容易出错,导致内存泄漏。cymem阻止您编写这些析构函数。相反,您应该这样编写

"""Example with Cymem.

Memory allocation is hidden behind the Pool class, which remembers the
addresses it gives out.  When the Pool object is garbage collected, all of
its addresses are freed.

We don't need to write MatrixArray.__dealloc__ or sparse_matrix_free,
eliminating a common class of bugs.
"""
from cymem.cymem cimport Pool

cdef struct SparseRow:
    size_t length
    size_t* indices
    double* values

cdef struct SparseMatrix:
    size_t length
    SparseRow* rows


cdef class MatrixArray:
    cdef size_t length
    cdef SparseMatrix** matrices
    cdef Pool mem

    def __cinit__(self, list py_matrices):
        self.mem = None
        self.length = 0
        self.matrices = NULL

    def __init__(self, list py_matrices):
        self.mem = Pool()
        self.length = len(py_matrices)
        self.matrices = <SparseMatrix**>self.mem.alloc(self.length, sizeof(SparseMatrix*))
        for i, py_matrix in enumerate(py_matrices):
            self.matrices[i] = sparse_matrix_init(self.mem, py_matrix)

cdef SparseMatrix* sparse_matrix_init_cymem(Pool mem, list py_matrix) except NULL:
    sm = <SparseMatrix*>mem.alloc(1, sizeof(SparseMatrix))
    sm.length = len(py_matrix)
    sm.rows = <SparseRow*>mem.alloc(sm.length, sizeof(SparseRow))
    cdef size_t i, j
    cdef dict py_row
    cdef size_t idx
    cdef double value
    for i, py_row in enumerate(py_matrix):
        sm.rows[i].length = len(py_row)
        sm.rows[i].indices = <size_t*>mem.alloc(sm.rows[i].length, sizeof(size_t))
        sm.rows[i].values = <double*>mem.alloc(sm.rows[i].length, sizeof(double))
        for j, (idx, value) in enumerate(py_row.items()):
            sm.rows[i].indices[j] = idx
            sm.rows[i].values[j] = value
    return sm

《Pool》类所做的一切只是记住它给出的地址。当《MatrixArray》对象被垃圾回收时,也会回收《Pool》对象,这会触发对《Pool.__dealloc__》的调用。然后,《Pool》会释放其所有的地址。这可以节省您回溯嵌套数据结构来释放它们,消除了常见错误类型。

自定义分配器

有时外部C库使用私有函数来分配和释放对象,但我们仍然希望有《Pool》的懒惰性。

from cymem.cymem cimport Pool, WrapMalloc, WrapFree
cdef Pool mem = Pool(WrapMalloc(priv_malloc), WrapFree(priv_free))

项目详情


下载文件

下载适合您平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

cymem-2.0.8.tar.gz (9.8 kB 查看哈希)

上传时间 源代码

构建分发

cymem-2.0.8-cp312-cp312-win_amd64.whl (39.1 kB 查看哈希)

上传于 CPython 3.12 Windows x86-64

cymem-2.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.7 kB 查看哈希值)

上传于 CPython 3.12 manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (44.2 kB 查看哈希值)

上传于 CPython 3.12 manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp312-cp312-macosx_11_0_arm64.whl (41.4 kB 查看哈希值)

上传于 CPython 3.12 macOS 11.0+ ARM64

cymem-2.0.8-cp312-cp312-macosx_11_0_arm64.whl (41.4 kB 查看哈希值)

上传于 CPython 3.12 macOS 10.9+ x86-64

cymem-2.0.8-cp311-cp311-win_amd64.whl (39.1 kB 查看哈希值)

上传于 CPython 3.11 Windows x86-64

cymem-2.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.3 kB 查看哈希值)

上传于 CPython 3.11 manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (44.2 kB 查看哈希值)

上传于 CPython 3.11 manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp311-cp311-macosx_11_0_arm64.whl (41.2 kB 查看哈希值)

上传于 CPython 3.11 macOS 11.0+ ARM64

cymem-2.0.8-cp311-cp311-macosx_10_9_x86_64.whl (41.9 kB 查看哈希值)

上传于 CPython 3.11 macOS 10.9+ x86-64

cymem-2.0.8-cp310-cp310-win_amd64.whl (39.0 kB 查看哈希值)

上传于 CPython 3.10 Windows x86-64

cymem-2.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.1 kB 查看哈希值)

上传时间: CPython 3.10 manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (44.0 kB 查看哈希值)

上传时间: CPython 3.10 manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp310-cp310-macosx_11_0_arm64.whl (41.0 kB 查看哈希值)

上传时间: CPython 3.10 macOS 11.0+ ARM64

cymem-2.0.8-cp310-cp310-macosx_10_9_x86_64.whl (41.6 kB 查看哈希值)

上传时间: CPython 3.10 macOS 10.9+ x86-64

cymem-2.0.8-cp39-cp39-win_amd64.whl (39.5 kB 查看哈希值)

上传时间: CPython 3.9 Windows x86-64

cymem-2.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.9 kB 查看哈希值)

上传时间: CPython 3.9 manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (44.8 kB 查看哈希值)

上传时间: CPython 3.9 manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp39-cp39-macosx_11_0_arm64.whl (41.6 kB 查看哈希值)

上传时间: CPython 3.9 macOS 11.0+ ARM64

cymem-2.0.8-cp39-cp39-macosx_10_9_x86_64.whl (42.1 kB 查看哈希值)

上传时间: CPython 3.9 macOS 10.9+ x86-64

cymem-2.0.8-cp38-cp38-win_amd64.whl (39.5 kB 查看哈希值)

上传时间: CPython 3.8 Windows x86-64

cymem-2.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.4 kB 查看哈希值)

上传时间: CPython 3.8 manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (44.4 kB 查看哈希)

上传时间 CPython 3.8 manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp38-cp38-macosx_11_0_arm64.whl (41.9 kB 查看哈希)

上传时间 CPython 3.8 macOS 11.0+ ARM64

cymem-2.0.8-cp38-cp38-macosx_10_9_x86_64.whl (42.5 kB 查看哈希)

上传时间 CPython 3.8 macOS 10.9+ x86-64

cymem-2.0.8-cp37-cp37m-win_amd64.whl (39.8 kB 查看哈希)

上传时间 CPython 3.7m Windows x86-64

cymem-2.0.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (46.7 kB 查看哈希)

上传时间 CPython 3.7m manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (45.0 kB 查看哈希)

上传时间 CPython 3.7m manylinux: glibc 2.17+ ARM64

cymem-2.0.8-cp37-cp37m-macosx_10_9_x86_64.whl (42.3 kB 查看哈希)

上传时间 CPython 3.7m macOS 10.9+ x86-64

cymem-2.0.8-cp36-cp36m-win_amd64.whl (46.5 kB 查看哈希)

上传时间 CPython 3.6m Windows x86-64

cymem-2.0.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (44.7 kB 查看哈希)

上传时间 CPython 3.6m manylinux: glibc 2.17+ x86-64

cymem-2.0.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (43.2 kB 查看哈希)

上传时间 CPython 3.6m manylinux: glibc 2.17+ ARM64