This slow code can be improved by changing the structure, but this is difficult to work around sometimes. The cause, I think, comes from classes stored in an array. I've heard memory views are used to link python and c arrays, but I'm still pretty new to this (only some python knowledge).
Is there a way to do the following efficiently?
An example class:
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef add_one(self):
self.value += 1
A slow function:
cdef unsigned long int i, ii
cdef unsigned long int loops = pow(10, 8)
cdef double value
addition_classes = np.array([None] * 10)
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addition_classes[ii].add_one()
Thank you very much for any suggestions!
There are some small things that you could do that should help a little. Really the line of code you want to speed up is addition_classes[ii].add_one(). If you use cython -a to see what's really happening under the hood you'll see that you're making a call to Pyx_GetItemInt, then PyObject_GetAttr, then PyObject_Call. You want to structure your code to avoid these 3 calls.
To avoid the GetItem Call, you'll want to use either numpy's buffer interface or memory views. This tells cython the structure of your array and allows it to pull items from the array more efficiently. In the example bellow I've used a memory view. If you do something similar, make sure that the array is in fact an array full of ClassWithAdditionFunction instances, otherwise you'll likely get a segfault.
To avoid the GetAttr call, declare a variable of type ClassWithAdditionFunction and make the method calls on that variable, that way cython knows that the variable has a compiled version of the method which it can use for faster calls.
Lastly you've already defined add_one with a cpdef method, but I would suggest also adding a return type. Normally we could just put void, but because this is a cpdef function and not a cdef function you could use int instead.
If you put all that together it should look something like:
import numpy as np
cimport cython
cdef class ClassWithAdditionFunction:
cdef double value
def __init__(self, double value):
self.value = value
cpdef int add_one(self):
self.value += 1
return 0
@cython.boundscheck(False)
@cython.wraparound(False)
def main():
cdef:
unsigned long int i, ii, loops = 10 ** 6
ClassWithAdditionFunction addInstance
double value, y
addition_classes = np.array([None] * 10)
cdef ClassWithAdditionFunction[:] arrayview = addition_classes
for i in range(len(addition_classes)):
addition_classes[i] = ClassWithAdditionFunction(value=0)
for i in range(loops/10):
for ii in range(10):
addInstance = arrayview[ii]
addInstance.add_one()
return None
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With