In the last post we’ve looked into how you can build PostgreSQL with Meson instead of the traditional way using configure/make. The reason for this was a thread on hackers which described the downsides of the traditional approach and explained the advantages of using Meson. Some of the advantages mentioned were the time required for the build, readability of the output and decreased time to run the regression tests. In this post we’ll run a few tests and check if this really improves the situation.
All the test we’ll run are executed on an AWS EC2 instance, the instance type is c5.xlarge. The operating system used is openSUSE Leap 15.4. You might ask yourself: Why openSUSE? Because there are not only Red Hat based distributions like Red Hat itself, Rocky Linux, Alma Linux, or Oracle Linux. Because there are not only Debian based distribution such as Debian itself, Ubuntu and all the other flavors. There is much more to choose from, and openSUSE is another great distribution, which usually is underrated.
All the required packages have already been installed, we’ll not go into the details of this. The only preparations that have been done are this:
[email protected]:~> git clone http://git.postgresql.org/git/postgresql.git
[email protected]:~> mv postgresql/ postgresql-meson
[email protected]:~> cp -pr postgresql-meson postgresql-traditional
[email protected]:~> ls -l
total 8
drwxr-xr-x 2 postgres postgres 6 Mar 15 2022 bin
drwxr-xr-x 7 postgres postgres 4096 Oct 18 19:30 postgresql-meson
drwxr-xr-x 7 postgres postgres 4096 Oct 18 19:30 postgresql-traditional
No magic here: The PostgreSQL source code was cloned and duplicated. There is no real reason to have the source code twice, this is just for readability of the scripts that follow. We can do the very same with just one copy of the source code. Note that the source code of PostgreSQL used here is the development version of PostgreSQL, currently 16devel.
We’ll start with the traditional way of building PostgreSQL, this is the script:
[email protected]:~> cat build-traditional.sh
#!/bin/bash
cd
BUILD_SOURCE="/home/postgres/postgresql-traditional"
BUILD_DIR="/home/postgres/build-traditional"
PGHOME="/home/postgres//pgsql-traditional"
SEGSIZE=2
BLOCKSIZE=8
rm -rf ${PGHOME}
rm -rf ${BUILD_DIR}
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
${BUILD_SOURCE}/configure --prefix=${PGHOME} \
--exec-prefix=${PGHOME} \
--bindir=${PGHOME}/bin \
--libdir=${PGHOME}/lib \
--sysconfdir=${PGHOME}/etc \
--includedir=${PGHOME}/include \
--datarootdir=${PGHOME}/share \
--datadir=${PGHOME}/share \
--with-pgport=5432 \
--with-perl \
--with-python \
--with-openssl \
--with-pam \
--with-ldap \
--with-libxml \
--with-libxslt \
--with-segsize=${SEGSIZE} \
--with-blocksize=${BLOCKSIZE} \
--with-llvm LLVM_CONFIG='/usr/bin/llvm-config' \
--with-uuid=ossp \
--with-lz4 \
--with-zstd \
--with-gssapi \
--with-systemd \
--with-icu \
--with-system-tzdata=/usr/share/zoneinfo
make -j 6 all
make -j 6 install
cd contrib
make -j 6 install
The reason we go for 6 jobs is, that Ninja, which is used later by Meson, defaults to 6 jobs on this system as well:
[email protected]:~> ninja --help
usage: ninja [options] [targets...]
if targets are unspecified, builds the 'default' target (see manual).
options:
--version print ninja version ("1.10.0")
-v, --verbose show all command lines while building
-C DIR change to DIR before doing anything else
-f FILE specify input build file [default=build.ninja]
-j N run N jobs in parallel (0 means infinity) [default=6 on this system]
-k N keep going until N jobs fail (0 means infinity) [default=1]
-l N do not start new jobs if the load average is greater than N
-n dry run (don't run commands but act like they succeeded)
-d MODE enable debugging (use '-d list' to list modes)
-t TOOL run a subtool (use '-t list' to list subtools)
terminates toplevel options; further flags are passed to the tool
-w FLAG adjust warnings (use '-w list' to list warnings)
Lets execute this three times and record the time it took for each build:
[email protected]:~> time ./build-traditional.sh
real 3m13.077s
user 9m54.203s
sys 1m0.454s
[email protected]:~> time ./build-traditional.sh
real 3m12.056s
user 9m55.699s
sys 1m0.765s
[email protected]:~> time ./build-traditional.sh
real 3m12.199s
user 9m57.002s
sys 1m0.192s
Quite consistent at 3 minutes and 12/13 seconds. This already is not so bad.
Lets see how Meson is performing. This is the script:
[email protected]:~> cat build-meson.sh
#!/bin/bash
cd
BUILD_SOURCE="/home/postgres/postgresql-meson"
BUILD_DIR="/home/postgres/build-meson"
PGHOME="/home/postgres//pgsql-meson"
SEGSIZE=2
BLOCKSIZE=8
rm -rf ${PGHOME}
rm -rf ${BUILD_DIR}
mkdir ${BUILD_DIR}
cd ${BUILD_DIR}
meson setup . ${BUILD_SOURCE}
meson configure -Dprefix=${PGHOME} \
-Dbindir=${PGHOME}/bin \
-Ddatadir=${PGHOME}/share \
-Dincludedir=${PGHOME}/include \
-Dlibdir=${PGHOME}/lib \
-Dsysconfdir=${PGHOME}/etc \
-Dpgport=5432 \
-Dplperl=enabled \
-Dplpython=enabled \
-Dssl=openssl \
-Dpam=enabled \
-Dldap=enabled \
-Dlibxml=enabled \
-Dlibxslt=enabled \
-Dsegsize=${SEGSIZE} \
-Dblocksize=${BLOCKSIZE} \
-Dllvm=enabled \
-Duuid=ossp \
-Dzstd=enabled \
-Dlz4=enabled \
-Dgssapi=enabled \
-Dsystemd=enabled \
-Dicu=enabled \
-Dsystem_tzdata=/usr/share/zoneinfo
ninja
ninja install
Same procedure, three times in a row:
[email protected]:~> time ./build-meson.sh
real 2m15.689s
user 7m14.784s
sys 0m38.827s
[email protected]:~> time ./build-meson.sh
real 2m14.565s
user 7m13.626s
sys 0m39.631s
[email protected]:~> time ./build-meson.sh
real 2m15.287s
user 7m14.789s
sys 0m39.323s
Almost a minute less, which is a nice improvement. This does not seem to be much if you only build once in a while, but if you build often I am sure you’ll appreciate the time savings. What makes it even more impressive is that you can change build options without re-configuring the whole tree. Lets say you want to rebuild with PAM disabled. All you need to do is this:
[email protected]:~/build-meson> time meson configure \
> -Dprefix=${PGHOME} \
> -Dbindir=${PGHOME}/bin \
> -Ddatadir=${PGHOME}/share \
> -Dincludedir=${PGHOME}/include \
> -Dlibdir=${PGHOME}/lib \
> -Dsysconfdir=${PGHOME}/etc \
> -Dpgport=5432 \
> -Dplperl=enabled \
> -Dplpython=enabled \
> -Dssl=openssl \
> -Dpam=disabled \
> -Dldap=enabled \
> -Dlibxml=enabled \
> -Dlibxslt=enabled \
> -Dsegsize=${SEGSIZE} \
> -Dblocksize=${BLOCKSIZE} \
> -Dllvm=enabled \
> -Duuid=ossp \
> -Dzstd=enabled \
> -Dlz4=enabled \
> -Dgssapi=enabled \
> -Dsystemd=enabled \
> -Dicu=enabled \
> -Dsystem_tzdata=/usr/share/zoneinfo
real 0m0.331s
user 0m0.307s
sys 0m0.021s
This is an almost immediate change and after rebuilding you have what you want:
[email protected]:~/build-meson> ninja
...
External libraries
bonjour : NO
bsd_auth : NO
gss : YES 1.19.2
icu : YES 65.1
ldap : YES
libxml : YES 2.9.14
libxslt : YES 1.1.34
llvm : YES 13.0.1
lz4 : YES 1.9.3
nls : YES
pam : NO
...
[email protected]:~/build-meson> ninja install
[email protected]:~> cd
[email protected]:~> pgsql-meson/bin/pg_config | grep -i pam
[email protected]:~>
This save the whole “configure” phase of the traditional build process.
Finally, lets look at the regression tests. With the traditional build system it goes like this (again, three times in a row):
[email protected]:~/build-traditional> pwd
/home/postgres/build-traditional
[email protected]:~/build-traditional> time make -j 6 check-world >/dev/null
...
real 1m7.388s
user 1m8.953s
sys 0m23.537s
[email protected]:~/build-traditional> time make -j 6 check-world >/dev/null
...
real 0m59.688s
user 0m59.238s
sys 0m21.616s
[email protected]:~/build-traditional> time make -j 6 check-world>/dev/null
...
real 1m2.093s
user 0m59.820s
sys 0m21.880s
For the Meson build it looks like this:
[email protected]:~/build-meson> time meson test
...
real 1m5.175s
user 0m48.452s
sys 0m18.157s
[email protected]:~/build-meson> time meson test
...
real 1m4.979s
user 0m49.441s
sys 0m18.468s
[email protected]:~/build-meson> time meson test
...
real 1m4.494s
user 0m49.510s
sys 0m17.946s
There is not much difference here, but the important point is the readability of the output. For make it looks like this all over the output:
...
running on port 61696 with PID 8991
============== creating database "contrib_regression" ==============
CREATE DATABASE
ALTER DATABASE
ALTER DATABASE
ALTER DATABASE
ALTER DATABASE
ALTER DATABASE
ALTER DATABASE
============== installing ltree ==============
CREATE EXTENSION
============== running regression test queries ==============
test ltree_plpython ... ok 31 ms
============== shutting down postmaster ==============
============== removing temporary instance ==============
=====================
All 1 tests passed.
=====================
make[2]: Leaving directory '/home/postgres/build-traditional/contrib/ltree_plpython'
ok 407 ms
test concurrent_ddl_dml ... ok 392 ms
test oldest_xmin ... ok 220 ms
test snapshot_transfer ... ok 23 ms
test subxact_without_top ... ok 21 ms
test concurrent_stream ... ok 172 ms
test twophase_snapshot ... ok 406 ms
test slot_creation_error ... ok 204 ms
test catalog_change_snapshot ... ok 35 ms
============== shutting down postmaster ==============
============== removing temporary instance ==============
======================
All 11 tests passed.
======================
...
Compare that to the output of Meson:
1/80 postgresql:setup / tmp_install OK 0.60s
2/80 postgresql:plpgsql / plpgsql/regress OK 3.53s
3/80 postgresql:plperl / plperl/regress OK 2.90s
4/80 postgresql:plpython / plpython/regress OK 4.61s
5/80 postgresql:adminpack / adminpack/regress OK 2.03s
6/80 postgresql:amcheck / amcheck/regress OK 3.63s
7/80 postgresql:basic_archive / basic_archive/regress OK 1.93s
8/80 postgresql:snapshot_too_old / snapshot_too_old/isolation OK 20.24s
9/80 postgresql:bloom / bloom/regress OK 3.03s
10/80 postgresql:main / main/regress OK 22.59s
11/80 postgresql:bool_plperl / bool_plperl/regress OK 2.49s
12/80 postgresql:btree_gin / btree_gin/regress OK 2.43s
13/80 postgresql:citext / citext/regress OK 1.99s
14/80 postgresql:btree_gist / btree_gist/regress OK 2.94s
15/80 postgresql:cube / cube/regress OK 2.01s
16/80 postgresql:dblink / dblink/regress OK 1.95s
17/80 postgresql:dict_int / dict_int/regress OK 1.81s
18/80 postgresql:dict_xsyn / dict_xsyn/regress OK 1.84s
19/80 postgresql:earthdistance / earthdistance/regress OK 1.93s
20/80 postgresql:file_fdw / file_fdw/regress OK 1.86s
21/80 postgresql:fuzzystrmatch / fuzzystrmatch/regress OK 1.80s
22/80 postgresql:hstore_plperl / hstore_plperl/regress OK 1.92s
23/80 postgresql:hstore / hstore/regress OK 2.56s
24/80 postgresql:hstore_plpython / hstore_plpython/regress OK 1.91s
25/80 postgresql:isn / isn/regress OK 2.07s
26/80 postgresql:jsonb_plperl / jsonb_plperl/regress OK 1.91s
27/80 postgresql:main / main/isolation OK 34.31s
28/80 postgresql:intarray / intarray/regress OK 3.43s
29/80 postgresql:jsonb_plpython / jsonb_plpython/regress OK 1.95s
30/80 postgresql:lo / lo/regress OK 2.04s
31/80 postgresql:ltree_plpython / ltree_plpython/regress OK 1.82s
32/80 postgresql:ltree / ltree/regress OK 2.17s
33/80 postgresql:pageinspect / pageinspect/regress OK 2.09s
34/80 postgresql:passwordcheck / passwordcheck/regress OK 1.82s
35/80 postgresql:pg_buffercache / pg_buffercache/regress OK 1.79s
36/80 postgresql:pg_freespacemap / pg_freespacemap/regress OK 1.77s
37/80 postgresql:pgcrypto / pgcrypto/regress OK 2.70s
38/80 postgresql:pgrowlocks / pgrowlocks/isolation OK 1.83s
39/80 postgresql:pg_stat_statements / pg_stat_statements/regress OK 1.84s
40/80 postgresql:pgstattuple / pgstattuple/regress OK 1.90s
41/80 postgresql:pg_surgery / pg_surgery/regress OK 1.90s
42/80 postgresql:pg_visibility / pg_visibility/regress OK 1.83s
43/80 postgresql:pg_trgm / pg_trgm/regress OK 2.60s
44/80 postgresql:pg_walinspect / pg_walinspect/regress OK 1.87s
45/80 postgresql:seg / seg/regress OK 1.86s
46/80 postgresql:tablefunc / tablefunc/regress OK 1.85s
47/80 postgresql:tcn / tcn/isolation OK 1.86s
48/80 postgresql:postgres_fdw / postgres_fdw/regress OK 4.41s
49/80 postgresql:tsm_system_rows / tsm_system_rows/regress OK 1.80s
50/80 postgresql:tsm_system_time / tsm_system_time/regress OK 1.84s
51/80 postgresql:test_decoding / test_decoding/isolation OK 4.39s
52/80 postgresql:unaccent / unaccent/regress OK 1.92s
53/80 postgresql:uuid-ossp / uuid-ossp/regress OK 1.79s
54/80 postgresql:test_decoding / test_decoding/regress OK 5.70s
55/80 postgresql:xml2 / xml2/regress OK 1.84s
56/80 postgresql:brin / brin/isolation OK 1.89s
57/80 postgresql:commit_ts / commit_ts/regress OK 1.79s
58/80 postgresql:dummy_index_am / dummy_index_am/regress OK 1.75s
59/80 postgresql:delay_execution / delay_execution/isolation OK 2.33s
60/80 postgresql:dummy_seclabel / dummy_seclabel/regress OK 1.80s
61/80 postgresql:plsample / plsample/regress OK 1.75s
62/80 postgresql:spgist_name_ops / spgist_name_ops/regress OK 1.84s
63/80 postgresql:test_bloomfilter / test_bloomfilter/regress OK 1.96s
64/80 postgresql:test_copy_callbacks / test_copy_callbacks/regress OK 1.83s
65/80 postgresql:test_ddl_deparse / test_ddl_deparse/regress OK 2.11s
66/80 postgresql:test_extensions / test_extensions/regress OK 1.91s
67/80 postgresql:test_ginpostinglist / test_ginpostinglist/regress OK 1.91s
68/80 postgresql:test_lfind / test_lfind/regress OK 1.74s
69/80 postgresql:test_oat_hooks / test_oat_hooks/regress OK 1.83s
70/80 postgresql:test_parser / test_parser/regress OK 1.89s
71/80 postgresql:test_pg_dump / test_pg_dump/regress OK 1.79s
72/80 postgresql:test_integerset / test_integerset/regress OK 4.88s
73/80 postgresql:test_predtest / test_predtest/regress OK 1.88s
74/80 postgresql:test_rbtree / test_rbtree/regress OK 1.94s
75/80 postgresql:test_regex / test_regex/regress OK 2.16s
76/80 postgresql:test_rls_hooks / test_rls_hooks/regress OK 1.81s
77/80 postgresql:unsafe_tests / unsafe_tests/regress OK 1.97s
78/80 postgresql:worker_spi / worker_spi/regress OK 2.11s
79/80 postgresql:test_shm_mq / test_shm_mq/regress OK 4.38s
80/80 postgresql:ecpg / ecpg/ecpg OK 3.49s
Ok: 80
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
This gives a much better overview. Instead of grepping through the individual test results, failed tests can easily be identified.
There for sure is much more to explore with Meson, but that’s it for now.