-
-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ENH: compiling with C99 support (non-MSVC only) #300
Conversation
All discrete filters must still be real valued, but complex data arrays are supported. Complex support is not yet extended to the CWT routines (cwt.h/cwt.c).
This is no longer needed as the C routines support complex dtypes Complex data type support was added to the SWT as well
for example: expected ‘__complex__ double *’ but argument is of type ‘struct __pyx_t_double_complex *’ This is because without the above definition, Cython uses its own complex types instead of those from C99
This is needed for MSVC compatiblity. This requires using defines in C (DEF in Cython) to control whether the complex valued routines are compiled. On the Python side, _have_c99_complex is used to control whether complex data can be passed directly to the Cython DWT and SWT routines or if the real and imaginary parts must be processed separately.
Codecov Report
@@ Coverage Diff @@
## master #300 +/- ##
==========================================
- Coverage 86.29% 83.56% -2.73%
==========================================
Files 21 21
Lines 3100 3268 +168
Branches 538 562 +24
==========================================
+ Hits 2675 2731 +56
- Misses 372 475 +103
- Partials 53 62 +9
Continue to review full report at Codecov.
|
The added complexity is manageable; a speedup of 1.5-2.5x is worth it I think. There are Detecting C99 support reliably is fairly tricky, and as you say this leaves out MinGW. I'm not sure if we should do this in the straightforward way you implemented it, or try to steal NumPy's implementation to try and cover more corner cases. https://github.com/numpy/numpy/blob/master/numpy/core/setup.py#L188 There's checks like these, but they don't go off a lot (if at all): The main unknown here is platforms other than Linux / Windows / OS X. |
I was not aware of how the C99 check was done in |
Yes I think so. Probably not worth it at this point, can always be done later if we get reports of detection issues. Since this is all green and looks sensible, I'll hit the green button. |
Thanks @grlee77! |
Glad to see this go in! I did look briefly at the numpy C99 code a while back, but couldn't commit the time to fully get it working here. I think this approach will work in most cases and we can revisit again later if needed. |
Yep, agreed. |
This PR improves performance in filtering complex-valued data by extending the C-level convolution routines to support C99
float complex
anddouble complex
. Typical speedup factors observed are in the range from 1.5-2.5.A drawback is some additional boilerplate on the C/Cython side to support the complex types. I work primarily with complex-valued data, so it is worth it to me, but would be interested in hearing from others if it is desirable for
PyWavelets
as a whole.I initially thought this would allow simplifying the python-side code by being able to remove all the special
np.iscomplexobj
checks (currently used to see if both real and imaginary components need to be processed independently). Unfortunately, due to lack of C99 complex in MSVC, this was not possible. Instead there is a new variable_have_c99_complex
which can be imported frompywt._c99_config
that indicates whether C99 support is present in the current build. I have tested locally with both gcc/linux and clang/OS X.There are many files modified, but conceptually it is fairly simple. I have outlined the basic approach below.
setup.py
defaults to enable C99 support only if
os.name == 'posix'
. The user can force C99 compilation by defining the environment variable USE_C99_COMPLEX (i.e. may be useful for a mingw build on Windows?).When C99 is enabled, it sets the C flag HAVE_C99_CPLX as well as setting the Cython macro CYTHON_CCOMPLEX to 1. The Cython flag allows Cython to use C99 instead of rolling its own complex support.
setup.py
creates the following two files related to C99 settings:pywt/_extensions/config.pxi
# defines HAVE_C99_CPLX as 1 or 0 for Cythonpywt/_c99_config.py
# defines _have_c99_complex as 1 or 0 for PythonFor the C code
Templates now use two separate defined values:
TYPE
andREAL_TYPE
REAL_TYPE
is used for the filters and is eitherfloat
ordouble
TYPE
is for the data/coefficients and can be:float
,double
,float complex
,double complex
when C99 is enabled, complex versions of the functions are generated as well
e.g.:
For the Cython code
in
_pywt.pxd
if HAVE_C99_COMPLEX is defined, a new fused type is defined as:On the Cython side the data/output use the fused type
cdata_t
while the filters use the typedata_t
. When c99 is not enabled they are the same.For the Python code
The Python code imports
_have_c99_complex
frompywt._c99_config
to determine whether it needs to process the real and imaginary components separately. When C99 is enabled allnp.iscomplexobj
special cases can be bypassed.Benchmarking Results
I ran the example from
demo/batch_processing.py
, but modify the data,cam
, to be complex as:I see a similar factor 1.5-2 speedup for larger and smaller transform sizes.