3

I have a pyx file containing the following code:

cimport cython
from libc.math cimport sqrt, abs
from libc.stdio cimport printf
from cython.parallel import prange

cdef double my_abs(double a):
    return sqrt(a*a)

cpdef loop(int a):
    cdef int i
    cdef int sum = 0
    for i in prange(a, nogil=True):
        printf("%s %i %s %f\n", "The square root of ", i, " is ", sqrt(i))
        #printf("%s %i %s %f\n", "The absolute value of ", i, " is ", abs(i))
        #printf("%s %i %s %f\n", "The absolute value of ", i, " is ", my_abs(i))
    return

Which fails to compile when I uncomment either of the two lines within the loop.

  1. Why does the libc.math abs function not play nice with nogil when others like sqrt, pow etc seem to?
  2. What do I have to add to my function (and .pxd file) to make it nogil? I have tried adding following this page https://lbolla.info/python-threads-cython-gil but it still won't compile.

This question is similar to:

Thanks in advance!

Community
  • 1
  • 1
Takoda
  • 354
  • 3
  • 12

2 Answers2

4

abs isn't in the C standard library math.h:

These convenience abs overloads are exclusive of C++. In C, abs is only declared in (and operates on int values).

I'm slightly surprised that Cython isn't complaining about you cimporting something that doesn't exist, but it'll be using the Python abs builtin (or possibly a slightly optimized Cython equivalent).

You want fabs instead.

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • well that certainly explains it! Thank you! Any idea about the second part? I have done some research on it but cant figure out the syntax. – Takoda Jul 26 '18 at 20:11
  • If it's `my_abs` you want to make nogil you just write `cdef double my_abs(double x) nogil:` – DavidW Jul 26 '18 at 21:19
1

As @DawidW has already pointed out, you should use fabs from math.h in your case.

I would like to elaborate a little bit about the causes (as I understand it):

  1. It is probably a bug: they can cimport abs from everywhere: cython, numpy and so on. Or don't import it at all - this stays the build-in abs.
  2. Cython tries to replace the Python-functionality through C-counterparts for build-in functions, the whole list is here. Depending on the type of the input Python-abs, C-abs, fabs, fabsf and so on are used.

For example:

%%cython 
from numpy cimport abs  # just for fun, "cimport" abs from numpy!     
cdef do_abs(object o, int i, double d):
    print(abs(o))  
    print(abs(i))
    print(abs(d))

Here, automatically:

  1. abs(o) is translated to __Pyx_PyNumber_Absolute (Python's functionality)
  2. abs(i) is translated to abs from stdlib.h
  3. abs(d) is translated to fabs from math.h

In the case of 2. and 3. gil is not needed, however from the Cython's point of view the Python-abs is in use (even if it is mapped to different functions) which is not defined as "nogil" and thus gil is needed.

What happens if we really cimport abs from clib.stdlib?

%%cython 
from libc.stdlib cimport abs
cdef do_abs(object o, int i, double d):
    print(abs(o))
    print(abs(i))
    print(abs(d))

We get:

  1. Cython tries to cast o to an C-integer (which might fail during the run time) and to call C-abs on its result.
  2. uses C-abs for the integer i
  3. doesn't compile, because Cython prevents the usage of a double value as integer (which is a Good Thing, there were spent a lot of debugging hours because C-compilers just cast the double-value to an integer (giving a warning if you are lucky)).

The main difference: using abs for the integer argument allows now nogil.


Some somewhat unrelated comments on your code:

A. Define cdef double my_abs(double a) nogil to be able to use it a nogil-section.

B. I'm not sure, Cython understand everything correctly when you pass a python-object as a varargin, because how should it deduce the right type to which the Python-object should be coerced? It is probably better to help it via an explicit cast, for example:

printf("%d %s", abs(i), <char *>"\n")
ead
  • 32,758
  • 6
  • 90
  • 153