Skip to main content

Manage calls to calloc/free through Cython

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cymem-2.0.3.tar.gz (51.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

cymem-2.0.3-cp38-cp38-win_amd64.whl (33.5 kB view details)

Uploaded CPython 3.8Windows x86-64

cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl (33.1 kB view details)

Uploaded CPython 3.8

cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl (31.8 kB view details)

Uploaded CPython 3.8macOS 10.9+ x86-64

cymem-2.0.3-cp37-cp37m-win_amd64.whl (32.7 kB view details)

Uploaded CPython 3.7mWindows x86-64

cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl (32.7 kB view details)

Uploaded CPython 3.7m

cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl (54.1 kB view details)

Uploaded CPython 3.7mmacOS 10.6+ Intel (x86-64, i386)

cymem-2.0.3-cp36-cp36m-win_amd64.whl (32.7 kB view details)

Uploaded CPython 3.6mWindows x86-64

cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl (32.7 kB view details)

Uploaded CPython 3.6m

cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl (54.0 kB view details)

Uploaded CPython 3.6mmacOS 10.6+ Intel (x86-64, i386)

cymem-2.0.3-cp35-cp35m-win_amd64.whl (31.8 kB view details)

Uploaded CPython 3.5mWindows x86-64

cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl (32.5 kB view details)

Uploaded CPython 3.5m

File details

Details for the file cymem-2.0.3.tar.gz.

File metadata

  • Download URL: cymem-2.0.3.tar.gz
  • Upload date:
  • Size: 51.0 kB
  • Tags: Source
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3.tar.gz
Algorithm Hash digest
SHA256 5083b2ab5fe13ced094a82e0df465e2dbbd9b1c013288888035e24fd6eb4ed01
MD5 cffbec980e45730f8ebc77777b74bfb0
BLAKE2b-256 ce8dd095bbb109a004351c85c83bc853782fc27692693b305dd7b170c36a1262

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 dd24848fbd75b17bab06408da6c029ba7cc615bd9e4a1f755fb3a090025fb922
MD5 33002c376e694c6c10001c1f82a8a0bf
BLAKE2b-256 8c1f43be34e4decc602fae2bda73b05525bc49deff44baeb95611b23a2929195

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl
  • Upload date:
  • Size: 33.1 kB
  • Tags: CPython 3.8
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp38-cp38-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 85b9364e099426bd7f445a7705aad87bf6dbb71d79e3802dd8ca14e181d38a33
MD5 f1e0ee4b014b3014f6cf22c288ec6af0
BLAKE2b-256 412dd24b1b980da72883a6a8dc5f9bdf48bcb892f3ac1579fb300045771693c3

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl
  • Upload date:
  • Size: 31.8 kB
  • Tags: CPython 3.8, macOS 10.9+ x86-64
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 622c20a57701d02f01a47e856dea248e112638f28c8249dbe3ed95a9702e3d74
MD5 02a84fbedc767545886cdd71731b6bbb
BLAKE2b-256 84a44d5685515ae321a9ee457c64456eddc56dcf65ba97e49bcfba19ef949d16

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 7f5ddceb12b73f7fd2e4398266401b6f887003740ccd18c989a2af04500b5f2b
MD5 c3ee1dc56ac6b5f1f2d15e8135cf71d4
BLAKE2b-256 84d135eab0c8cc9fd9432becaf3e90144762b3201a45079e62c47a8ae8739763

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: CPython 3.7m
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp37-cp37m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 c288a1bbdf58c360457443e5297e74844e1961e5e7001dbcb3a5297a41911a11
MD5 333ee5ff66d49404bd846ab8620f18e9
BLAKE2b-256 e1796ce05ecf4d50344e29749ea7db7ddf427589228fb8fe89b29718c38c27c5

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl.

File metadata

  • Download URL: cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl
  • Upload date:
  • Size: 54.1 kB
  • Tags: CPython 3.7m, macOS 10.6+ Intel (x86-64, i386)
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp37-cp37m-macosx_10_6_intel.whl
Algorithm Hash digest
SHA256 d7505c500d994f11662e5595f5002251f572acc189f18944619352e2636f5181
MD5 19688ab119155c1fb26783078603e359
BLAKE2b-256 6334a8b682ee9b57db35a5fe4e179b77c1bda0f6a09745669a99cfc27aa2bed7

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp36-cp36m-win_amd64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp36-cp36m-win_amd64.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: CPython 3.6m, Windows x86-64
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 719f04a11ca709fc2b47868070d79fccff77e5d502ff32de2f4baa73cb16166f
MD5 96722be9aac23dd62a05dd5ab6f947b0
BLAKE2b-256 ddec904b4741879a2a280a40d5bf0b61449a20d1f75281e14ebee06566f7765b

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 32.7 kB
  • Tags: CPython 3.6m
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp36-cp36m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 7236252bed70f37b898933dcf8aa875d0829664a245a272516f27b30439df71c
MD5 86e9b677c281bb751f3c24eb92ee91f6
BLAKE2b-256 e7b53e1714ebda8fd7c5859f9b216e381adc0a38b962f071568fd00d67e1b1ca

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl.

File metadata

  • Download URL: cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl
  • Upload date:
  • Size: 54.0 kB
  • Tags: CPython 3.6m, macOS 10.6+ Intel (x86-64, i386)
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp36-cp36m-macosx_10_6_intel.whl
Algorithm Hash digest
SHA256 6f4cb689a9552e9e13dccc89203c8ab09f210a7ffb92ce27c384a4a0be27b527
MD5 c0307d45ecb3f5478b92e0c5e081df0b
BLAKE2b-256 b51b05d8fb2884ab06ecc77830f8d797d97a2c0ed4679922d2fd00bab499cbb7

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp35-cp35m-win_amd64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp35-cp35m-win_amd64.whl
  • Upload date:
  • Size: 31.8 kB
  • Tags: CPython 3.5m, Windows x86-64
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp35-cp35m-win_amd64.whl
Algorithm Hash digest
SHA256 cd21ec48ee70878d46c486e2f7ae94b32bfc6b37c4d27876c5a5a00c4eb75c3c
MD5 8986bb92ac11cb1625d898af17fbe342
BLAKE2b-256 6e7ca348594e1a2639c0d7e6b5009620a5771ef82a29bdc51f8b638b323224d7

See more details on using hashes here.

File details

Details for the file cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl.

File metadata

  • Download URL: cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl
  • Upload date:
  • Size: 32.5 kB
  • Tags: CPython 3.5m
  • Uploaded using 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

File hashes

Hashes for cymem-2.0.3-cp35-cp35m-manylinux1_x86_64.whl
Algorithm Hash digest
SHA256 f4f19af4bca81f11922508a9dcf30ce1d2aee4972af9f81ce8e5331a6f46f5e1
MD5 21186a2c7ca3d537e014c26252ec7124
BLAKE2b-256 015a0d44fd9ef1765a2e17a566de21ac26c13b02a7a1564106c5c4fd8935c866

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page