Bruce Nielson
Bruce Nielson

Reputation: 823

Cython Basics: How to speed up common c functions like random and math functions?

I am working on learning Cython (See How to Cythonize a Python Class with an attribute that is a function?)

I have the following function that I want to speed up, but Cython says both lines are going through python:

cdef selectindex(float fitnesspref):
    r = np.random.rand()
    return int(log(r) / log(fitnesspref))

So I need to get a random number (I originally just used random() built in Python call, but switched to Numpy hoping it was faster or would Cythonize better. Then I need to take the log of two floats, divide them, and then return the result back as an integer. Nothing too difficult, and should be a great question that people can then use later. I'm struggling to find simple solutions to all the info I need via the Cython docs or Google. I would have thought this was like in every tutorial, but I'm not having a lot of luck.

Looking around though, I can't find an nice easy solution. For example: Canonical way to generate random numbers in Cython

Is there a best, but simpler, way to Cythonize a function like this?

As a side note, I had a similar problem with abs() but then after I changed everything to be cdefs it seemed to automatically switch to using the C version.

Here is the results of the HTML generated by Cython: enter image description here

Another common one I keep bumping into is:

start = time.time()

i.e. is there a simple way to get start and end times using C code to speed that up? I have that line there in an inner loop, so it's slowing things down. But it's really important to what I'm trying to do.

Update 1: I'm trying to follow the suggestions in the comments and they don't seem to work out like I'd expect (which is why this all confuses me in the first place.) For example, here is a c version of random that I wrote and compiled. Why is it still yellow? enter image description here

Update 2: Okay, I researched it further and here is why it won't compile all the way to C:

enter image description here

It's doing a check to make sure I'm not dividing by zero, even though it's a constant and can't ever be zero. How do you get rid of that?

Upvotes: 1

Views: 804

Answers (1)

Bruce Nielson
Bruce Nielson

Reputation: 823

For anyone that follows me, here were the final answers:

Many common functions, including len() are already built in. If you switch it to use carrays it automatically compiles to C. See this link.

For the rest, the following imports were required:

from libc.stdlib cimport rand, RAND_MAX
from libc.math cimport log

To replace calls to random.random():

@cython.cdivision(True)
cdef float crandom() except -1: 
    return  <float>rand() / <float>RAND_MAX

To replace calls to random.randint():

@cython.cdivision(True)
cdef int crandint(int lower, int upper) except -1:
    return (rand() % (upper - lower + 1)) + lower

My selectindex function got rewritten as:

cdef int selectindex(float fitnesspref, int popsize) except -1:
    cdef float r
    cdef int val
    r = crandom() + 0.00000000001
    return <int>(log(r) / log(fitnesspref))

That 0.00000000001 was necessary because C and Python behave slightly differently here. The Python version never returns a straight up zero apparently out of the random calls but the crandom does every so often. And a log of zero is undefined. This might be because my c version is only working with a limited number of ints as it's starting point.

I never did come up with a way to replace time.time().

I hope this helps some newbie following after me.

Upvotes: 3

Related Questions