Questions related to Meson+Pythran
paugier opened this issue · 6 comments
Happy new year Serge and @rgommers !
I'm trying to build my packages using Pythran through Transonic with Meson. I tried to follow https://pythran.readthedocs.io/en/latest/MANUAL.html#integration-into-a-python-package and what is done in Scipy. Overall we now have a working solution (described in particular in these documents: Packaging when using Transonic and Build Fluidsim), however, I'm still unsure about what should be done for few questions related to Pythran.
Warning for @rgommers: beware if you look at the ugly things we do in meson.build files with Transonic. Python source directories with their meson.build
files are generated from other meson.build
files. I realize that it does not follow the Meson philosophy but it works and it's convenient. Anyway, this issue is not about that.
Pythran's documentation is quite minimalist about Meson and it might be useful to add few notes.
~/.pythranrc
With Meson, Pythran is used only to produce C++ code. Hence, many of the options in the .pythranrc files are not used. Anyway, it seems reasonable that a build of a package would be reproducible and not depend at all on a ~/.pythranrc file. I don't know if it is possible to tell Pythran to not consider ~/.pythranrc. I tried with PYTHRANRC="" pythran ...
but it seems that pythran is happy with this and continues to consider ~/.pythranrc.
[compiler]
section
It would be nice to have a short note (or just a link) explaining how to do with meson-python what is usually done with the [compiler]
section of Pythran configuration files. What is recommended? Just environment variables?
complex_hook
The only Pythran option that I really use affecting C++ code generation is complex_hook
. So with Meson, we call Pythran with --config pythran.complex_hook=pythran_complex_hook
and we have a Meson option pythran-complex-hook
.
In https://pythran.readthedocs.io/en/latest/MANUAL.html#customizing-your-pythranrc there is
complex_hook:
Set this to True for faster and still Numpy-compliant complex multiplications. Not very portable, but generally works on Linux.
I don't know if it means that it could be reasonable to build wheels uploaded on PyPI to set pythran-complex-hook to true on Linux and false elsewhere? Also I don't know what would be the best way to do that with meson-python...
USE_XSIMD
and -Ofast -march=native
It seems to me that Scipy build does not use USE_XSIMD
. I wonder if it is because it would not be portable enough? However, for my case, I'd like to be able when installing from source to use this flag and also -Ofast -march=native
. Moreover, it is not clear to me how this should be done with Meson-python.
Happy new year @paugier!
Pythran's documentation is quite minimalist about Meson and it might be useful to add few notes.
Sure, happy to submit a PR after we finish discussing in this issue.
Anyway, it seems reasonable that a build of a package would be reproducible and not depend at all on a ~/.pythranrc file.
+1. I'm not sure if it's possible to tell Pythran to not look at ~/.pythranrc
. There probably should be a CLI flag for that at least?
It would be nice to have a short note (or just a link) explaining how to do with meson-python what is usually done with the
[compiler]
section of Pythran configuration files. What is recommended? Just environment variables?
For compiler selection the standard CC
/CXX
environment variables indeed. And also CFLAGS
/CXXFLAGS
and LDFLAGS
are honored. The other things in [compiler]
look like they all belong in meson.build
files.
compiler.blas
should probably be ignored explicitly via -DPYTHRAN_BLAS_NONE
like SciPy does by default. And arguably pythran-openblas
should be removed completely from Pythran and PyPI, it's 5 years old and 20 minor releases of OpenBLAS behind. OpenBLAS 0.3.6 is very buggy and if an extension is also using numpy
having a second OpebLAS copy in memory may lead to issues. It's only used for a single function, numpy.dot
, with a few CBLAS functions called. And the include <cblas.h>
isn't portable (e.g., for MKL it's mkl_cblas.h
not cblas.h
).
I don't know if it means that it could be reasonable to build wheels uploaded on PyPI to set pythran-complex-hook to true on Linux and false elsewhere? Also I don't know what would be the best way to do that with meson-python...
I have no knowledge of what complex_hook
does, but shipping an sdist and wheels with meson-python shouldn't be hard. In your meson.build
file, something like (adding a complete example from SciPy here, the if host_machine().system() == 'linux'
is the added part here):
cmd = [pythran, '-E', '@INPUT@', '-o', '@OUTDIR@/_rbfinterp_pythran.cpp']
if host_machine().system() == 'linux'
cmd += ['--config pythran.complex_hook=pythran_complex_hook']
endif
_rbfinterp_pythran = custom_target('_rbfinterp_pythran',
output: ['_rbfinterp_pythran.cpp'],
input: '_rbfinterp_pythran.py',
command: cmd,
)
_rbfinterp_pythran = py.extension_module('_rbfinterp_pythran',
_rbfinterp_pythran,
cpp_args: cpp_args_pythran,
dependencies: [pythran_dep, np_dep],
install: true,
subdir: 'scipy/interpolate'
)
It seems to me that Scipy build does not use
USE_XSIMD
. I wonder if it is because it would not be portable enough? However, for my case, I'd like to be able when installing from source to use this flag and also-Ofast -march=native
. Moreover, it is not clear to me how this should be done with Meson-python.
I need to double-check this in SciPy, will circle back. Pythran is certainly looking for xsimd
by default in the SciPy build, since I remember fixing a couple of issues with that.
To add the flag, it looks like you can simply add -DUSE_XSIMD
to cmd
in the example above? The include paths should already be okay if you copied what SciPy has in scipy/meson.build
.
Thanks for your answers @rgommers.
Regarding USE_XSIMD
, I think we need cpp_args_pythran += ['-DUSE_XSIMD']
, which is not done for Scipy. @serge-sans-paille do you think -DUSE_XSIMD
can be used to build wheels uploaded on PyPI?
For Fluidsim, I now use:
option(
'native',
type: 'boolean',
value: false,
description: 'Performance oriented and not portable build',
)
option(
'use-xsimd',
type: 'boolean',
value: true,
description: 'Turns on xsimd vectorization',
)
option(
'pythran-complex-hook',
type: 'combo',
choices: ['os-dependent', 'true', 'false'],
value: 'os-dependent',
description: 'Pythran complex_hook option',
)
and (in the main meson.build
file):
use_pythran = backend.contains('pythran')
if use_pythran
incdir_numpy = run_command(
py,
[
'-c',
'''import os
import numpy as np
try:
incdir = os.path.relpath(np.get_include())
except Exception:
incdir = np.get_include()
print(incdir)'''
],
check: true
).stdout().strip()
inc_np = include_directories(incdir_numpy)
np_dep = declare_dependency(include_directories: inc_np)
incdir_pythran = run_command(
py,
[
'-c',
'''import os
import pythran;
incdir = os.path.dirname(pythran.__file__)
try:
incdir = os.path.relpath(incdir)
except Exception:
pass
print(incdir)'''
],
check: true
).stdout().strip()
pythran = find_program('pythran', native: true)
cpp_args_pythran = [
'-DENABLE_PYTHON_MODULE',
'-D__PYTHRAN__=3',
'-DPYTHRAN_BLAS_NONE'
]
if get_option('use-xsimd') == true
# xsimd is unvendored from pythran by conda-forge, and due to a compiler
# activation bug the default <prefix>/include/ may not be visible (see
# gh-15698). Hence look for xsimd explicitly.
xsimd_dep = dependency('xsimd', required: false)
pythran_dep = declare_dependency(
include_directories: incdir_pythran,
dependencies: xsimd_dep,
)
cpp_args_pythran += ['-DUSE_XSIMD']
else
pythran_dep = declare_dependency(
include_directories: incdir_pythran,
)
endif
pythran_complex_hook = get_option('pythran-complex-hook')
if pythran_complex_hook == 'os-dependent'
pythran_complex_hook = host_machine.system() == 'linux'
endif
if get_option('native')
cpp_args_pythran += ['-march=native', '-Ofast']
endif
endif
I wouldn't be against some helpers given by meson-python to simplify this, but I don't know if it would be possible (one can't define functions or macros in Meson).
I defined the option native
because it seems to me that it's an important feature for a computational Python project to be able to be compiled in approximately two modes :
- to build portable wheels,
- to build for the specific CPU used for compilation (
-march=native
).
Is there a better way to do that with Meson and meson-python ?
With #2171, one can ignore ~/.pythranrc
by calling Pythran in Meson files with:
_rbfinterp_pythran = custom_target('_rbfinterp_pythran',
output: ['_rbfinterp_pythran.cpp'],
input: '_rbfinterp_pythran.py',
command: cmd,
env: ['PYTHRANRC='],
)
A good point is that it can already be done (with no effect for pythran<=0.15.0).
Regarding
USE_XSIMD
, I think we needcpp_args_pythran += ['-DUSE_XSIMD']
, which is not done for Scipy. @serge-sans-paille do you think-DUSE_XSIMD
can be used to build wheels uploaded on PyPI?
Ah right - I think that's the difference between using xsimd
's vectorization/SIMD code paths, or the scalar/fallback paths? We certainly had build failures in SciPy on xsimd
going missing in conda-forge, so it is needed.
xsimd is unvendored from pythran by conda-forge
This unvendoring was reverted ~8 months ag, as it was not a good idea: conda-forge/pythran-feedstock#76
[...] 2. to build for the specific CPU used for compilation (
-march=native
).
The two "build modes" make perfect sense, but I'm not sure that it needs a package-defined build option. You can simply put -march=native
in CXXFLAGS
or pass them with the cpp_args
compiler option. So this works naturally already without custom code in your own meson.build
files.
I wouldn't be against some helpers given by meson-python to simplify this, but I don't know if it would be possible (one can't define functions or macros in Meson).
For dependency detection I have the same clumsiness with dependency('numpy')
. I plan to make that "just work" as a one-liner by adding a numpy.pc
pkg-config file and a numpy-config
script, so that there is no need to invoke Python to get at the get_include()
.
For dependency detection I have the same clumsiness with
dependency('numpy')
. I plan to make that "just work" as a one-liner by adding anumpy.pc
pkg-config file and anumpy-config
script, so that there is no need to invoke Python to get at theget_include()
.
I'll note that this is done in numpy/numpy#25730 and mesonbuild/meson#12799, it should be available in Meson 1.4.0 / NumPy 2.0.0. Once that's in use without hiccups, it'd be nice to do the same for Pythran. Or do it now, since it's fairly safe (it's more or less a copy of what already worked for pybind11
).