build: add a meson build configuration - #1910
Conversation
|
To be clear: the build here does not work. In Linux at least I get a completed build but some functions are missing. Probably not much is needed to get to a basic working state though. |
|
Maybe as a first step this can be brought up to parity with and replace CMake. I'd rather not have three parallel build systems. To replace our autotools setup, we'd need to support all its capabilities and someone would have to commit to maintaining the build system in the medium term. |
If we change build systems at all, I think they should be replaced. In other words, Meson would replace both CMake and Autotools. To answer your question, yes, under the right circumstances.
Apart from this example, why would it be more nice? I have gotten a lot of people seemingly being afraid of Autotools, but I have never understood why. So why?
So the most important things here: What was the difference, and what C flags where you using in each case?
I mean, we can establish run-time detection of CPU's. It is not hard. For x86, we can simply utilize the |
|
I could maintain a Meson build system if a PR was made that such both Autotools and CMake could be removed. Hence, the functionalities of Meson should include:
|
All these things will be doable somehow. I will check what looks like a good way though... I'll see if I can actually get a working build first though :)
From python-flint's perspective there are two primary advantages that would come from Flint using either meson or cmake rather than autotools. The same considerations would apply to many other downstream projects as well though:
Projects that use autotools don't work properly as subprojects because they are opaque from the outside and cannot keep track automatically of when it is necessary to rerun With meson a rebuild can keep track of when there are changes to the meson.build files or anything else or when built files should be removed, when dependencies have been updated to a new version etc. This is like if Not all projects can use e.g. meson as their primary build system because build system tooling creates a bootstrapping problem. For example GMP and MPFR are dependencies of gcc itself. Likewise Python is a dependency of meson so it would be awkward for CPython to use meson as its own build system. A solution to that from python-flint's perspective is that we can add external wrapdb files for GMP and MPFR (other people want this as well mesonbuild/wrapdb#97). In the case of Flint there is no bootstrapping issue though and the ideal solution for python-flint would be if Flint used either meson or cmake. If Flint used meson then it could also track GMP and MPFR as subprojects using the wrap file mechanism. In fact I think if Flint did that then it would not be necessary for python-flint or other downstream projects to do it explicitly because GMP and MPFR could just become recursive subprojects from python-flint's perspective (I haven't tested this). |
Currently I get: I guess some symbols are not being exported correctly. |
|
Fair points. Let me know if you need help interpreting |
Actually this does give a working build I think at least on Linux. I was just linking python-flint against the wrong libflint.so. The fix is that you have to actually install libflint.so after building it! diff --git a/meson.build b/meson.build
index ae7cd95a1..1169a801b 100644
--- a/meson.build
+++ b/meson.build
@@ -28,6 +28,7 @@ install_headers(headers_all, subdir: 'flint')
libflint = library('flint', c_files_all,
dependencies: flint_deps,
include_directories: include_directories('src'),
+ install: true
)
pkg_mod = import('pkgconfig') |
I'm not explicitly setting any flags so just the default ones are being used. This is how the meson build compiles the C files by default: This is how make compiles them The main difference for build time is After fixing it to use For a no-op build i.e. running Probably basic build speed like this is not a significant factor here. What is more important is that it is a more reproducible build each time and that the reproducibility does not come at the cost of slower builds. You would get a big gain in speed in situations where you would otherwise need to use Also you can configure multiple build directories differently like: Now you can have incremental rebuilds in each differently configured build directory without them clobbering each other: With autotools after |
|
Yeah, I really like the rebuilding times you presented. Could you just present the exact building times you get from Meson vs Make for building the whole library (not including the configuration process)? You can go with CFLAGS="-O0" for simplicity. I just want to make sure that building with Meson for one-time builds are not 30% slower or something. |
|
And I just want to make it clear that I will not merge something that does not superset-ish what Autotools currently does. I know this is somewhat of a big task to take on, just so that you are aware of my requirements. Make sure that you exactly know what the Autotools stuff is doing. And, once more, I am here to help you with the interpretation of the Autoconf stuff. |
|
These are timings from a fresh git clone using this PR as it stands. The timings are on a relatively slow Linux machine with 4 cores and build using Ubuntu's stock gcc in both cases. Both builds give a usable This is the timing for a full rebuild in a fresh clone with $ git clone https://github.com/oscarbenjamin/flint.git
$ cd flint/
$ git checkout pr_meson
$ ./bootstrap.sh
$ ./configure
$ time make -j5 > make-output.txt
...
real 5m20.621s
user 18m48.493s
sys 2m8.120sThis is the full rebuild timing with meson+ninja: $ git clone https://github.com/oscarbenjamin/flint.git
$ cd flint
$ git checkout pr_meson
$ ./bootstrap.sh
$ ./configure
$ meson setup build
$ time meson compile -C build
...
real 5m43.528s
user 17m48.237s
sys 2m24.565sIn this run meson is 7% slower than make but I've run these both a few times today and seen a lot of variability. Many times meson seemed faster but it all takes a bit too long for me to just run it many times and average. For the quoted timings I was careful not to use the computer while the build ran although the screen did go to sleep briefly during the meson build so I woke it up. These are warm cache timings for a no-op build like running $ time meson compile -C build
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /home/oscar/.pyenv/shims/ninja -C /home/oscar/current/active/flint/tmp/flint/build
ninja: Entering directory `/home/oscar/current/active/flint/tmp/flint/build'
ninja: no work to do.
real 0m1.329s
user 0m1.035s
sys 0m0.355s
$ time make
make: Nothing to be done for 'all'.
real 0m6.010s
user 0m5.764s
sys 0m0.242sSo in this case These are timings after $ touch src/gr_mpoly.h
$ time make
CC gr/mpoly.c
CC gr_mpoly/add.c
CC gr_mpoly/combine_like_terms.c
CC gr_mpoly/equal.c
CC gr_mpoly/fit_bits.c
CC gr_mpoly/fit_length.c
CC gr_mpoly/fit_length_fit_bits.c
CC gr_mpoly/fit_length_reset_bits.c
CC gr_mpoly/gen.c
CC gr_mpoly/get_coeff_scalar_fmpz.c
CC gr_mpoly/get_coeff_scalar_ui.c
CC gr_mpoly/init.c
CC gr_mpoly/inlines.c
CC gr_mpoly/is_canonical.c
CC gr_mpoly/mul.c
CC gr_mpoly/mul_johnson.c
CC gr_mpoly/mul_monomial.c
CC gr_mpoly/mul_scalar.c
CC gr_mpoly/neg.c
CC gr_mpoly/push_term.c
CC gr_mpoly/randtest_bits.c
CC gr_mpoly/randtest_bound.c
CC gr_mpoly/set.c
CC gr_mpoly/set_coeff_scalar_fmpz.c
CC gr_mpoly/set_coeff_scalar_ui.c
CC gr_mpoly/set_scalar.c
CC gr_mpoly/sort_terms.c
CC gr_mpoly/sub.c
CC gr_mpoly/write.c
Building libflint.so.20.0.0
real 0m10.885s
user 0m9.677s
sys 0m1.055s
$ touch src/gr_mpoly.h
$ time meson compile -C build
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /home/oscar/.pyenv/shims/ninja -C /home/oscar/current/active/flint/tmp/flint/build
ninja: Entering directory `/home/oscar/current/active/flint/tmp/flint/build'
[30/30] Linking target libflint.so
real 0m5.194s
user 0m8.241s
sys 0m1.769sIt looks like they both built 30 targets and meson did it in 5 seconds vs 10 seconds for make. These are timings for $ touch src/fmpz/fmpz.c
$ time make
CC fmpz/fmpz.c
Building libflint.so.20.0.0
real 0m6.411s
user 0m5.942s
sys 0m0.467s
$ touch src/fmpz/fmpz.c
$ time meson compile -C build
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /home/oscar/.pyenv/shims/ninja -C /home/oscar/current/active/flint/tmp/flint/build
ninja: Entering directory `/home/oscar/current/active/flint/tmp/flint/build'
[2/2] Linking target libflint.so
real 0m3.550s
user 0m2.434s
sys 0m1.020sBoth built 2 targets (fmpz.o and libflint.so) and it took 3 seconds for meson vs 6 seconds for make. |
|
These are timings for a full build in a fresh clone but on a faster Mac laptop that has a 12 core M3 CPU. On this computer I have a few local changes meaning that the meson build can get to the end but it fails with a link error, due to some assembly preprocessor defines not being correct. I think that the timings are approximately comparable any way though because linking is only the last step. This is the timing with make: $ git clone https://github.com/oscarbenjamin/flint.git
$ cd flint/
$ git checkout pr_meson
$ ./bootstrap.sh
$ ./configure
$ time make -j13 > make-output.txt
...
make -j13 > make-output.txt 213.18s user 102.57s system 841% cpu 37.527 totalThis is the timing with meson: $ git clone https://github.com/oscarbenjamin/flint.git
$ cd flint/
$ git checkout pr_meson
$ git apply ../mac-fixes.diff # some local fixes...
$ ./bootstrap.sh
$ ./configure
$ meson setup build
$ time meson compile -C build
...
meson compile -C build 189.25s user 86.31s system 710% cpu 38.780 totalIn this case the meson build is about 11% faster than make but the final link step did not complete: The problem is something to with this preprocessor define: flint/src/mpn_extras/mul_basecase.c Lines 275 to 291 in 940102e The diff applied to make that get as far as it did was: diff --git a/meson.build b/meson.build
index 6a73b0dbe..31734a767 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,10 @@
project('FLINT', 'c',
version: '3.1.0',
license: 'LGPL3+',
- default_options: ['buildtype=release'],
+ default_options: [
+ 'buildtype=release',
+ 'c_std=c11',
+ ],
)
cc = meson.get_compiler('c')
@@ -12,6 +15,14 @@ mpfr_dep = dependency('mpfr', version: '>= 4.1.0')
flint_deps = [gmp_dep, mpfr_dep, m_dep]
+add_project_arguments(
+ '-march=armv8-a',
+ '-DBUILDING_FLINT',
+ '-DFLINT_NOSTDIO',
+ '-DFLINT_NOSTDARG',
+ '-Werror=implicit-function-declaration',
+ language: 'c')
+
subdir('src')
headers_all = []
diff --git a/src/meson.build b/src/meson.build
index 66c0451ab..2a9d0d4fd 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1947,15 +1947,15 @@ c_files_all_subdir = [
'fft/mul_truncate_sqrt2.c',
'fft/negmod_2expp1.c',
'fft/normmod_2expp1.c',
- #'fft_small/default_ctx.c',
- #'fft_small/fmpz_poly_mul.c',
- #'fft_small/mpn_helpers.c',
- #'fft_small/mpn_mul.c',
- #'fft_small/mulmod_statisfies_bounds.c',
- #'fft_small/nmod_poly_mul.c',
- #'fft_small/sd_fft.c',
- #'fft_small/sd_fft_ctx.c',
- #'fft_small/sd_ifft.c',
+ 'fft_small/default_ctx.c',
+ 'fft_small/fmpz_poly_mul.c',
+ 'fft_small/mpn_helpers.c',
+ 'fft_small/mpn_mul.c',
+ 'fft_small/mulmod_statisfies_bounds.c',
+ 'fft_small/nmod_poly_mul.c',
+ 'fft_small/sd_fft.c',
+ 'fft_small/sd_fft_ctx.c',
+ 'fft_small/sd_ifft.c',
'fft/split_bits.c',
'fmpq/add.c',
'fmpq/addmul.c',
@@ -5275,7 +5275,6 @@ c_files_all_subdir = [
'qsieve/poly.c',
'qsieve/primes_init.c',
'qsieve/square_root.c',
- 'test/main.c',
'thread_pool/clear.c',
'thread_pool/distribute_work.c',
'thread_pool/find_work.c', |
|
Timings are looking very good. I think we can afford a small drop in a one-time build while we gain a lot in rebuilds! Thank you for looking into this. |
What would be great is if you could just summarise from a high level what it is that configure is doing to modify the sources and also how that affects the generated Makefile. Right now I am (inadvisedly?) trying to make the meson build work after configure has run so that I don't need to replicate its effect on the sources yet. However it seems that configure has encoded some information into the Makefile in relation to fft_small that I don't immediately understand to be able to replicate in meson. |
|
Regarding the wrong assembly linking: Is this with Autotools or is this with Meson? |
With meson. It works fine with autotools. |
|
Oh, I see. If it is with Meson then it should be expected (unless you have gotten that far already). |
I definitely haven't :) |
|
We (= me = Albin) use M4 to make writing assembly a little bit easier. One such things is making the assembler write the correct symbol. MacOS typically prefix their symbols with an underscore, which we test during the configuration. |
|
So if you write |
|
There are a couple of other things that is needed to be able to write assembly, but I will try to write a list of what the whole Autotools process is doing. |
|
Note that I will herein only describe the process for Autotools. Any local File structure
|
Things to consider when replacing
|
|
I don't think I missed anything, but of course I left some (more or less obvious) things unsaid. Let me know if there is something that needs to be explained in more detail. |
In meson it is not possible to simply set different compiler flags on a per file basis but this kind of case where a few files use different flags can be handled by building them into an intermediate static library that is then linked into the end product:
This is all doable. Some kinds of assembly have explicit support in meson but in general you can use custom targets to run any commands that need to be run if needed.
With meson this is taken care of automatically by the build configuration. Something like: It doesn't use environment variables etc except when configuring.
Adding source files automatically is disallowed in meson. There are workarounds but the intention is that files are listed explicitly and this is what enables it to be fast and reproducible. Dependencies among .c and .h files are determined automatically by ninja but the files themselves must be listed explicitly for this to work. We can easily make a Python script that could update this for all but one or two flint modules (I imagine fft_small or mpn_extras would be exceptions).
I won't show the full output but: $ meson configure build-dir
Core properties:
Source dir /Users/enojb/work/dev/flint/tmp/flint
Build dir /Users/enojb/work/dev/flint/tmp/flint/build
Main project options:
Core options Current Value Possible Values Description
-------------- ------------- --------------- -----------
auto_features auto [enabled, disabled, auto] Override value of all 'auto' features
backend ninja [ninja, vs, vs2010, vs2012, vs2013, Backend to use
vs2015, vs2017, vs2019, vs2022, xcode,
none]
buildtype release [plain, debug, debugoptimized, release, Build type to use
minsize, custom]
...Those are built-in options but Flint-specific custom options would get listed there as well.
By default it looks like what I showed above: $ meson compile -C build
INFO: autodetecting backend as ninja
INFO: calculating backend command to run: /home/oscar/.pyenv/shims/ninja -C /home/oscar/current/active/flint/tmp/flint/build
ninja: Entering directory `/home/oscar/current/active/flint/tmp/flint/build'
[30/30] Linking target libflint.soWhen you actually run that it shows a running counter like
It shows at the final link step. For a full build it's something like
This was a meson bug that was fixed specifically because someone was using meson to build Flint:
These things will all be possible somehow. It might be that some things that are handled in the Makefile would need to be a separate Python script or something.
The simple way to do this is just $ meson setup new-build-dir
$ meson configure new-build-dir -Dfoo=bar
$ meson compile -C new-build-dirNote that meson does everything out-of-tree. It only creates/modifies files etc inside the build directory and leaves the source tree itself untouched.
This is just |
I don't like this at all. Seems like a very basic thing that high performance libraries want to utilize. Hopefully we can do it gracefully.
Yeah, don't be too worried about this. I'm down for only doing it manually.
Haha, that's funny! Nice that is was being fixed.
Alright. I just want to make it clear that I would like to avoid users having to call something else than Although I have my worries, I think this all sounds good. I'm looking forward to see the progress for this. |
|
And thank you for looking into this! |
I think all of these things are handled natively by meson's test command:
Testing won't work with the meson.build here yet though because I haven't added any configuration to build the tests. |
|
There is a meson bug about test executables being built by default: A workaround is to have a configure option for building tests: It should be possible to make it so that |
This is incomplete but I am putting it here as a talking point:
Would there be any interest in flint using meson as a build system or as an alternative experimental build system?
For downstream usecases it would be nicer if Flint used something like meson rather than autotools et al. For python-flint for example if flint used either cmake or meson then it could be incorporated as a meson subproject after flintlib/python-flint#129 is merged.
From my limited testing it seems faster to build Flint with meson+ninja rather than configure+make. Currently though the meson configuration here does not build on all platforms and it is probably compiling things differently so any timing comparison is not meaningful at this stage.
Another potential advantage of using meson could be using its (experimental) SIMD module which facilitates using runtime detection for things like AVX:
https://mesonbuild.com/Simd-module.html
Runtime detection of CPU capabilities is what would be needed for python-flint to leverage AVX because it is not possible for Python wheel tags to be more fine-grained than e.g.
x86_64.The meson configuration code shown here is incomplete because it does not handle options and things and also it is deliberately written in the simplest possible way.
To test building this with meson first install meson and ninja (e.g.
pip install meson ninja) and then:You can then install with
Arguments like
--prefixshould be passed tomeson setup.A usable implementation of building with meson would need to work without the bootstrap/configure steps. I cannot personally turn the changes in this PR into a complete replacement of configure etc but I could turn it into a usable experimental alternative.
Another possibility is that I could submit the same files to meson's wrapdb database so that other projects can build flint using meson without the meson build files being part of the flint source tree:
https://mesonbuild.com/Wrapdb-projects.html