Skip to main content

cymem: A Cython Memory Helper

cymem provides two small memory-management helpers for Cython. They make it easy to tie memory to a Python object's life-cycle, so that the memory is freed when the object is garbage collected.

Azure Pipelines pypi Version conda Version Python wheels

Overview

The most useful is cymem.Pool, which acts as a thin wrapper around the calloc function:

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

The Pool object saves the memory addresses internally, and frees them when the object is garbage collected. Typically you'll attach the Pool to some cdef'd class. This is particularly handy for deeply nested structs, which have complicated initialization functions. Just pass the Pool object into the initializer, and you don't have to worry about freeing your struct at all — all of the calls to Pool.alloc will be automatically freed when the Pool expires.

Installation

Installation is via pip, and requires Cython.

pip install cymem

Example Use Case: An array of structs

Let's say we want a sequence of sparse matrices. We need fast access, and a Python list isn't performing well enough. So, we want a C-array or C++ vector, which means we need the sparse matrix to be a C-level struct — it can't be a Python class. We can write this easily enough in 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)

We wrap the data structure in a Python ref-counted class at as low a level as we can, given our performance constraints. This allows us to allocate and free the memory in the __cinit__ and __dealloc__ Cython special methods.

However, it's very easy to make mistakes when writing the __dealloc__ and sparse_matrix_free functions, leading to memory leaks. cymem prevents you from writing these deallocators at all. Instead, you write as follows:

"""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

All that the Pool class does is remember the addresses it gives out. When the MatrixArray object is garbage-collected, the Pool object will also be garbage collected, which triggers a call to Pool.__dealloc__. The Pool then frees all of its addresses. This saves you from walking back over your nested data structures to free them, eliminating a common class of errors.

Custom Allocators

Sometimes external C libraries use private functions to allocate and free objects, but we'd still like the laziness of the Pool.

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

Metadata

Release files for cymem 2.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cymem 2.0.3
File Size Uploaded
cymem-2.0.3.tar.gz 51.0 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for cymem 2.0.3
File
cymem-2.0.3-cp38-cp38-win_amd64.whl CPython 3.8 CPython 3.8 Windows x86-64 Details
cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl CPython 3.8 CPython 3.8 Linux glibc 2.5+ x86-64 Details
cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl CPython 3.8 CPython 3.8 macOS 10.9+ x86-64 Details
cymem-2.0.3-cp37-cp37m-win_amd64.whl CPython 3.7 CPython 3.7 pymalloc Windows x86-64 Details
cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl CPython 3.7 CPython 3.7 pymalloc Linux glibc 2.5+ x86-64 Details
cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl CPython 3.7 CPython 3.7 pymalloc macOS 10.6+ Intel (x86-64, i386) Details
cymem-2.0.3-cp36-cp36m-win_amd64.whl CPython 3.6 CPython 3.6 pymalloc Windows x86-64 Details
cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl CPython 3.6 CPython 3.6 pymalloc Linux glibc 2.5+ x86-64 Details
cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl CPython 3.6 CPython 3.6 pymalloc macOS 10.6+ Intel (x86-64, i386) Details
cymem-2.0.3-cp35-cp35m-win_amd64.whl CPython 3.5 CPython 3.5 pymalloc Windows x86-64 Details
cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl CPython 3.5 CPython 3.5 pymalloc Linux glibc 2.5+ x86-64 Details

Total release size: 452.5 kB

Release files / cymem-2.0.3.tar.gz

Download URL cymem-2.0.3.tar.gz
Size 51.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5083b2ab5fe13ced094a82e0df465e2dbbd9b1c013288888035e24fd6eb4ed01
BLAKE2b-256 checksum
How to use checksums
ce8dd095bbb109a004351c85c83bc853782fc27692693b305dd7b170c36a1262
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp38-cp38-win_amd64.whl

Download URL cymem-2.0.3-cp38-cp38-win_amd64.whl
Size 33.5 kB
Tags CPython 3.8 Windows x86-64
SHA-256 checksum
How to use checksums
dd24848fbd75b17bab06408da6c029ba7cc615bd9e4a1f755fb3a090025fb922
BLAKE2b-256 checksum
How to use checksums
8c1f43be34e4decc602fae2bda73b05525bc49deff44baeb95611b23a2929195
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl

Download URL cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl
Size 33.1 kB
Tags CPython 3.8 Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
85b9364e099426bd7f445a7705aad87bf6dbb71d79e3802dd8ca14e181d38a33
BLAKE2b-256 checksum
How to use checksums
412dd24b1b980da72883a6a8dc5f9bdf48bcb892f3ac1579fb300045771693c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl

Download URL cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl
Size 31.8 kB
Tags CPython 3.8 macOS 10.9+ x86-64
SHA-256 checksum
How to use checksums
622c20a57701d02f01a47e856dea248e112638f28c8249dbe3ed95a9702e3d74
BLAKE2b-256 checksum
How to use checksums
84a44d5685515ae321a9ee457c64456eddc56dcf65ba97e49bcfba19ef949d16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp37-cp37m-win_amd64.whl

Download URL cymem-2.0.3-cp37-cp37m-win_amd64.whl
Size 32.7 kB
Tags CPython 3.7 CPython 3.7 pymalloc Windows x86-64
SHA-256 checksum
How to use checksums
7f5ddceb12b73f7fd2e4398266401b6f887003740ccd18c989a2af04500b5f2b
BLAKE2b-256 checksum
How to use checksums
84d135eab0c8cc9fd9432becaf3e90144762b3201a45079e62c47a8ae8739763
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl

Download URL cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl
Size 32.7 kB
Tags CPython 3.7 CPython 3.7 pymalloc Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
c288a1bbdf58c360457443e5297e74844e1961e5e7001dbcb3a5297a41911a11
BLAKE2b-256 checksum
How to use checksums
e1796ce05ecf4d50344e29749ea7db7ddf427589228fb8fe89b29718c38c27c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl

Download URL cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl
Size 54.1 kB
Tags CPython 3.7 CPython 3.7 pymalloc macOS 10.6+ Intel (x86-64, i386)
SHA-256 checksum
How to use checksums
d7505c500d994f11662e5595f5002251f572acc189f18944619352e2636f5181
BLAKE2b-256 checksum
How to use checksums
6334a8b682ee9b57db35a5fe4e179b77c1bda0f6a09745669a99cfc27aa2bed7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp36-cp36m-win_amd64.whl

Download URL cymem-2.0.3-cp36-cp36m-win_amd64.whl
Size 32.7 kB
Tags CPython 3.6 CPython 3.6 pymalloc Windows x86-64
SHA-256 checksum
How to use checksums
719f04a11ca709fc2b47868070d79fccff77e5d502ff32de2f4baa73cb16166f
BLAKE2b-256 checksum
How to use checksums
ddec904b4741879a2a280a40d5bf0b61449a20d1f75281e14ebee06566f7765b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl

Download URL cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl
Size 32.7 kB
Tags CPython 3.6 CPython 3.6 pymalloc Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
7236252bed70f37b898933dcf8aa875d0829664a245a272516f27b30439df71c
BLAKE2b-256 checksum
How to use checksums
e7b53e1714ebda8fd7c5859f9b216e381adc0a38b962f071568fd00d67e1b1ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl

Download URL cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl
Size 54.0 kB
Tags CPython 3.6 CPython 3.6 pymalloc macOS 10.6+ Intel (x86-64, i386)
SHA-256 checksum
How to use checksums
6f4cb689a9552e9e13dccc89203c8ab09f210a7ffb92ce27c384a4a0be27b527
BLAKE2b-256 checksum
How to use checksums
b51b05d8fb2884ab06ecc77830f8d797d97a2c0ed4679922d2fd00bab499cbb7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp35-cp35m-win_amd64.whl

Download URL cymem-2.0.3-cp35-cp35m-win_amd64.whl
Size 31.8 kB
Tags CPython 3.5 CPython 3.5 pymalloc Windows x86-64
SHA-256 checksum
How to use checksums
cd21ec48ee70878d46c486e2f7ae94b32bfc6b37c4d27876c5a5a00c4eb75c3c
BLAKE2b-256 checksum
How to use checksums
6e7ca348594e1a2639c0d7e6b5009620a5771ef82a29bdc51f8b638b323224d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8

Release files / cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl

Download URL cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl
Size 32.5 kB
Tags CPython 3.5 CPython 3.5 pymalloc Linux glibc 2.5+ x86-64
SHA-256 checksum
How to use checksums
f4f19af4bca81f11922508a9dcf30ce1d2aee4972af9f81ce8e5331a6f46f5e1
BLAKE2b-256 checksum
How to use checksums
015a0d44fd9ef1765a2e17a566de21ac26c13b02a7a1564106c5c4fd8935c866
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/2.0.0 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.38.0 CPython/3.6.8
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page