1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
cimport cython
from libc.stdint cimport uintptr_t
cdef extern from *:
int unlikely(int) nogil # defined by Cython
cdef inline void* align(void* ptr, size_t alignment):
"""
Round up ``ptr`` to the nearest multiple of ``alignment``, which
must be a power of 2
"""
cdef uintptr_t x = <uintptr_t>ptr
x = (x + alignment - 1) & ~(alignment - 1)
return <void*>x
@cython.final
cdef class MemoryAllocator:
cdef size_t n
cdef size_t size
cdef void** pointers
cdef void* static_pointers[16] # If n <= 16, store pointers here
cdef void* malloc(self, size_t size) except? NULL
cdef void* calloc(self, size_t nmemb, size_t size) except? NULL
cdef void* allocarray(self, size_t nmemb, size_t size) except? NULL
cdef void* realloc(self, void* ptr, size_t size) except? NULL
cdef void* reallocarray(self, void* ptr, size_t nmemb,
size_t size) except? NULL
cdef int resize(self, size_t new_size) except -1
cdef void** find_pointer(self, void* ptr) except NULL
cdef inline int enlarge_if_needed(self) except -1:
r"""
Enlarge the list of pointers if needed such that there is at
least one free entry.
"""
if unlikely(self.n >= self.size):
return self.resize(self.size * 2)
cdef inline void* aligned_malloc(self, size_t alignment,
size_t size) except? NULL:
r"""
Returns new aligned pointer. Stores it to be automatically freed later.
Alignment must be a power of two.
.. NOTE::
If you want to allocate multiple (small) aligned arrays with the
same alignment and really want to be memory efficient, you can
allocate one large aligned array instead.
"""
cdef size_t extra = alignment - 1
return align(self.malloc(size + extra), alignment)
cdef inline void* aligned_calloc(self, size_t alignment, size_t nmemb,
size_t size) except? NULL:
r"""
Returns new aligned pointer. Stores it to be automatically freed later.
Alignment must be a power of two.
.. NOTE::
If you want to allocate multiple (small) aligned arrays with the
same alignment and really want to be memory efficient, you can
allocate one large aligned array instead.
"""
# Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1
# ⇔ extra * size >= alignment - 1
# ⇔ extra >= ceil( (alignment - 1) / size)
# ⇔ extra >= (alignment - 1 + size - 1) // size
cdef size_t extra = (alignment + size - 2) // size
return align(self.calloc(nmemb + extra, size), alignment)
cdef inline void* aligned_allocarray(self, size_t alignment, size_t nmemb,
size_t size) except? NULL:
r"""
Returns new aligned pointer. Stores it to be automatically freed later.
Alignment must be a power of two.
.. NOTE::
If you want to allocate multiple (small) aligned arrays with the
same alignment and really want to be memory efficient, you can
allocate one large aligned array instead.
"""
# Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1
# ⇔ extra * size >= alignment - 1
# ⇔ extra >= ceil( (alignment - 1) / size)
# ⇔ extra >= (alignment - 1 + size - 1) // size
cdef size_t extra = (alignment + size - 2) // size
return align(self.allocarray(nmemb + extra, size), alignment)
|