How to figure out why cython-izing code slows it down? - python

We have some code written in python that uses a few classes that are really just "structs" -- instances of these classes just have a bunch of fields in them and no methods. Example:
class ResProperties:
def __init__(self):
self.endDayUtilities = 0
self.marginalUtilities = []
self.held = 0
self.idleResource = True
self.experience = 0.0
self.resSetAside = 0
self.unitsGatheredToday = 0
Our main code uses a bunch of instances of this class.
To speed up the code, I thought I'd cython-ized this class:
cdef class ResProperties:
cdef public float endDayUtilities
cdef public list marginalUtilities
cdef public int held
cdef public int idleResource
cdef public float experience
cdef public int resSetAside
cdef public int unitsGatheredToday
def __init__(self):
self.endDayUtilities = 0
# etc: code just like above.
However, the result is that the code runs something like 25% slower now!
How do I figure out what is causing the code to run slower now?
Thanks.

You converted these classes to Cython but are still using them from Python code?
Conversion of data from C to Python and back is going to incur overhead. For example, your endDayUtilities member is a C-style float. When you access it from Python, a float() object must be constructed before your Python code can do anything with it. The same thing has to happen in reverse when you assign to that attribute from Python.
Off the top of my head, I'd estimate the performance overhead of these data conversions at... oh, about 25%. :-)
You're not going to see a performance boost until you move some of your code that uses that data to Cython. Basically, the more you can stay in C-land, the better you'll do. Going back and forth will kill you.
As another, simpler approach, you might want to try Psyco or PyPy instead of Cython.

Related

Storing unsafe C derivative of temporary Python reference Cython

Although I few similar questions already being asked, but I couldn't get head around on how to fix.
Basically I have this function:
Module one.pyx:
cdef char *isInData(data, dataLength):
cdef char *ret = <char *>malloc(200)
memset(ret, 0x00, 200)
if (b"Hello" in data and b"World" in data):
strcat(ret, b"Hello World!")
return ret
Module two.pyx:
import one
from libc.stdlib cimport malloc,realloc, free
cdef char *tempo():
cdef char *str
str = one.isInData(b"Hello what is up World", 23)
# do some stuff
free(str)
Issue occurs on line str = one.isInData("Hello what is up World", 23), I am assuming that as soon as isInData->ret is assigned to str. isInData->ret is deleted which causes this issue. But annyone help me on how to fix this?
import one
This line does a Python import of one. It doesn't know about any cdef functions defined in one (or even that one is a Cython module...). Therefore it assumes that isInData is a Python object that it can look up and that it'll be a callable returning another Python object.
cdf char* str = some_python_function() is unsafe because str points into the Python object. However the Python object is only a temporary and is most likely freed almost immediately.
What you mean is:
cimport one
which tells Cython that it's a Cython module that you're importing, and it should expect to find the declarations at compile-time. You'll probably need to write a pxd file to give the declarations.
I haven't looked in detail at the rest of your code so don't know if you are handling C strings right. But in general you may find it easier just to use Python strings rather than messing around with C string handling.

Is it possible to pass a numpy array by reference into a cpdef function?

Is it possible to write a Cython function where a numpy array is passed by reference (perhaps a memory view?) and can I use such function in Python?
I tried:
cpdef void my_function(bytes line, int& counter, np.ndarray[np.uint32_t, ndim=2]& sums):
...
...
Here counter and sums would be passed by reference. I am interested in the latter (but will happily accept critics and suggestions regarding both).
The compiler throws:
Reference base type cannot be a Python object
I get the same message with cdef either and I don't understand the full etiology of the problem.
Yes - sort of.
A Numpy array is a Python object. All Python objects are passed "by reference" anyway (i.e. if you change the contents of a Numpy array inside the function you change it outside).
Therefore you just do
cpdef void my_function(bytes line, int& counter, np.ndarray[np.uint32_t, ndim=2] sums):
Specifying it as a C++ reference doesn't really make much sense. For a Python object there's quite a bit of detail that Cython hides from you.
However, cpdef functions are designed to be callable from Python and Cython. Python ints (and other numeric types) are immutable. Therefore changing counter will not work as you think when called from Python, and if it did what you hoped it'd potentially break the interpreter.
I'd therefore avoid using references in cpdef functions (and probably mostly avoid cpdef functions completely - it's usually better just to decide on def or cdef)

Memory efficiency when exposing C struct with Cython

I am looking to use Cython to reduce the memory requirements of a data structure that is stored in a Python dictionary millions of times. Right now, I have implemented this as a simple Cython class in a pyx file:
cdef class FooBar:
cdef public int x
cdef public int y
def __init__(self, x, y):
self.x = x
self.y = y
I use this in another pyx file to populate a Python dictionary that has millions of keys:
python_dict[key] = FooBar(x, y)
This provides a substantial memory savings compared to using a namedtuple or other Python solution as my actual class stores 6 integers and thus required 40 bytes of memory (6*4 + 16 bytes for PyObject_Head). However, the 16 bytes overhead is substantial and my question is if there is a better way to do this. My understanding is that I can't directly expose a C struct since this would be type cast to a Python dictionary requiring far more memory. The python_dict dictionary is used extensively in my primarily Python code. Is there a way to avoid the PyObject_Head overhead without having to move a lot of the implementation over to pure Cython/C?

Complex conversion to Python Complex

I'm currently writing a wrapper for a C++ project that use std::complex<double>, available in cython as libcpp.complex.complex[double].
However there is no implicit conversion between this and Python complex, I'm trying to find the best way to do this conversion.
The obvious is to use
cdef libcpp.complex.complex[double] x = ...
X = complex(x.real(),x.imag()
And
cdef complex Y = ...
cdef libcpp.complex.complex[double] y = libcpp.complex.complex[double](Y.real, Y.imag)
And
cdef libcpp.complex.complex[double] z
cdef complex Z = ...
z.real(Z.real)
z.imag(Z.imag)
But is there a better way, preferably to make cython do the conversion automatically.
For one this occurs in some cpdef functions, and I would like to avoid using Python complex in calls from cython for improved speed.
However as far as I can tell cython can't do this conversion implicitly, and thus I can't avoid using the Python complex for code that can be called from Python or C++.
This functionality will be released in the next version of Cython.
https://github.com/cython/cython/commit/2c3c04b25620c437ce41d7851e7581eb1f0c313c

python iterate over dynamically allocated Cython array

I'm writing a python wrapper to a C class and I'm allocating memory using PyMem_Malloc as explained here
cdef class SomeMemory:
cdef double* data
def __cinit__(self, size_t number):
# allocate some memory (uninitialised, may contain arbitrary data)
self.data = <my_data_t*> PyMem_Malloc(number * sizeof(my_data_t))
if not self.data:
raise MemoryError()
Then I import and use the class in another script using:
import SomeMemory
sm = SomeMemory(10)
I now would like to access the elements of self.data but I encounter 2 problems
if I type self.data and hit enter, the ipython kernel crashes
if I try to loop on self data
like:
for p in self.data:
print p
I get an error that self.data is not iterable.
How can I access self.data? Do I need to cast the elements to my_data_t first?
(Heavily edited in light of the updated question)
So - my understanding is that self.data needs to be declared as public to access it from Python:
cdef class SomeMemory:
cdef public double* data
# also make sure you define __dealloc__ to remove the data
However, that doesn't seem to be enforced here.
However, your real problems are that Python doesn't know what to do with an object of type double *. It's certainly never going to be iterable because the information about when to stop is simply not stored (so it will always go off the end.
There are a range of better alternatives:
you store your data as a Python array (see http://docs.cython.org/src/tutorial/array.html for a guide). You have quick access to the raw c pointer from within Cython if you want, but also Python knows what to do with it
Code follows
from cpython cimport array as c_array
from array import array
cdef class SomeMemory:
cdef c_array.array data
def __cinit__(self, size_t number):
self.data = array('d', some initial value goes here)
You store your data as a numpy array. This is possibly slightly more complicated, but has much the same advantages. I won't give an example, but it's easier to find.
You use Cython's typed memory view: http://docs.cython.org/src/userguide/memoryviews.html. The advantage of this is that it lets you control the memory management yourself (if that's absolutely important to you). However, I'm not 100% sure that you can access them cleanly in Python (too lazy to test!)
You wrap the data in your own class implementing __getitem__ and __len__ so it can be used as an iterator. This is likely more trouble that it's worth.
I recommend the first option, unless you have good reason for one of the others.

Categories