Skip to content
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

postgresql: support JIT compilation for version >= 11 #124804

Closed
wants to merge 1 commit into from

Conversation

andir
Copy link
Member

@andir andir commented May 28, 2021

Motivation for this change

PostgreSQL (from 11 onwards AFAICT) supports JIT compilation of
(frequently used) queries. This should provide a speedup to some queries
where operations such as comparing rows is a bottleneck.

For JIT support we have to build PostgreSQL with with the --with-llvm
config flag and pass both clang and llvm into the build environment.
PostPostgreSQL then calls llvm-config to obtain the required compiler
flags.

Things done
  • compiled postgresl 11 with the feature
  • tested that JIT actually works
  • verify that all the postgresql version we provide still build

@andir
Copy link
Member Author

andir commented May 29, 2021

I've changed the postgresql test to verify that JIT is working (through the plan analyzer) and that it does still return the right results (by running a query with JIT being on).

PostgreSQL (from 11 onwards AFAICT) supports JIT compilation of
(frequently used) queries. This should provide a speedup to some queries
where operations such as comparing rows is a bottleneck.

For JIT support we have to build PostgreSQL with with the --with-llvm
config flag and pass both clang and llvm into the build environment.
PostPostgreSQL then calls llvm-config to obtain the required compiler
flags.
@andir andir changed the base branch from master to staging May 29, 2021 08:58
@marsam
Copy link
Contributor

marsam commented May 29, 2021

Thanks for working on this.
You may be interested in the previous attempt #38698, specifically in commits dcd9455 8caf523 91841ff

@risicle
Copy link
Contributor

risicle commented May 30, 2021

Did a dirty cherry-pick of this to master so I could build it (in <24h) and I get a failure on macos 10.15:

...
gzipping man pages under /nix/store/cyj6isr81b41a7269jzrdc5467xy9zbn-postgresql-11.11-man/share/man/
strip is /nix/store/9xawf09c90761xkxv8r1b55ja46x4wr3-cctools-binutils-darwin-949.0.1/bin/strip
patching script interpreter paths in /nix/store/cyj6isr81b41a7269jzrdc5467xy9zbn-postgresql-11.11-man
output '/nix/store/g4k7qcs7fff5vn34msn8kyq7mmsqyfh2-postgresql-11.11' is not allowed to refer to the following paths:
  /nix/store/d4p8v32sivk3zj3jklx3irswbwwigxcd-clang-wrapper-7.1.0
error: build of '/nix/store/rzimpd88ysc69x39m437kx3k9vm2y1aq-postgresql-11.11.drv' failed

@andir
Copy link
Member Author

andir commented May 31, 2021

Did a dirty cherry-pick of this to master so I could build it (in <24h) and I get a failure on macos 10.15:

...
gzipping man pages under /nix/store/cyj6isr81b41a7269jzrdc5467xy9zbn-postgresql-11.11-man/share/man/
strip is /nix/store/9xawf09c90761xkxv8r1b55ja46x4wr3-cctools-binutils-darwin-949.0.1/bin/strip
patching script interpreter paths in /nix/store/cyj6isr81b41a7269jzrdc5467xy9zbn-postgresql-11.11-man
output '/nix/store/g4k7qcs7fff5vn34msn8kyq7mmsqyfh2-postgresql-11.11' is not allowed to refer to the following paths:
  /nix/store/d4p8v32sivk3zj3jklx3irswbwwigxcd-clang-wrapper-7.1.0
error: build of '/nix/store/rzimpd88ysc69x39m437kx3k9vm2y1aq-postgresql-11.11.drv' failed

Likely fixable with some of the commits that @marsam mentioned above. I'll try to get to them soon.

@dasJ
Copy link
Member

dasJ commented Jun 20, 2021

Yes they found a solution:

     # JIT is only supported on Linux, for now. (Darwin may build, but must be
     # tested).
     jitEnabled = atLeast "11" && enableJitSupport && deps.stdenv.isLinux;

@dasJ
Copy link
Member

dasJ commented Jun 20, 2021

This is what I did in my overlay so the closure doesn't grow too much (not a whole gigabyte). Parts of these are stolen from the old PR.
There is no runtime dependency on cc anymore.

self: super: {
  postgresql_13 = (super.postgresql_13.overrideAttrs (oA: {
    nativeBuildInputs = (oA.nativeBuildInputs or []) ++ [ super.llvmPackages_latest.llvm super.nukeReferences super.patchelf ];
    configureFlags = (oA.configureFlags or []) ++ [ "--with-llvm" ];
    postPatch = ''
      ${oA.postPatch or ""}

      # Force lookup of jit stuff in $out instead of $lib
      substituteInPlace src/backend/jit/jit.c --replace pkglib_path \"$out/lib\"
      substituteInPlace src/backend/jit/llvm/llvmjit.c --replace pkglib_path \"$out/lib\"
      substituteInPlace src/backend/jit/llvm/llvmjit_inline.cpp --replace pkglib_path \"$out/lib\"
    '';

    postInstall = ''
      ${oA.postInstall or ""}

      # Move the bitcode and libllvmjit.so library out of $lib; otherwise, every client that
      # depends on libpq.so will also have libLLVM.so in its closure too, bloating it
      moveToOutput "lib/bitcode" "$out"
      moveToOutput "lib/llvmjit*" "$out"

      # In the case of JIT support, prevent a retained dependency on clang-wrapper
      substituteInPlace "$out/lib/pgxs/src/Makefile.global" --replace ${super.llvmPackages_latest.stdenv.cc}/bin/clang clang
      nuke-refs $out/lib/llvmjit_types.bc $(find $out/lib/bitcode -type f)

      # Stop out depending on the default output of llvm
      substituteInPlace $out/lib/pgxs/src/Makefile.global \
        --replace ${super.llvmPackages_latest.llvm.out}/bin "" \
        --replace '$(LLVM_BINPATH)/' ""

      # Stop out depending on the -dev output of llvm
      substituteInPlace $out/lib/pgxs/src/Makefile.global \
        --replace ${super.llvmPackages_latest.llvm.dev}/bin/llvm-config llvm-config \
        --replace -I${super.llvmPackages_latest.llvm.dev}/include ""

      # Stop lib depending on the -dev output of llvm
      rpath=$(patchelf --print-rpath $out/lib/llvmjit.so)
      nuke-refs -e $out $out/lib/llvmjit.so
      # Restore the correct rpath
      patchelf $out/lib/llvmjit.so --set-rpath "$rpath"
    '';
  })).override {
    stdenv = super.llvmPackages_latest.stdenv;
  };
}

Size comparison

No JIT JIT Diff
out size 25M 97M +72M
out closure size 305.3M 693.4M +388.1M
lib size 6.0M 5.9M -0.1M
lib closure size 46.7M 46.5M -0.2M

New dependencies of out

  • /nix/store/f3qba2h3b9krj1ir343f9r8m73by5rn3-llvm-11.1.0-lib
  • /nix/store/niplk2w8g0ri0n8h07zyp1l3cbnzv7a2-glibc-2.32-46-dev

@stale
Copy link

stale bot commented Jan 8, 2022

I marked this as stale due to inactivity. → More info

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jan 8, 2022
@RaitoBezarius
Copy link
Member

Can I do something to get this merged? It is an awesome feature.

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Dec 1, 2022
@risicle
Copy link
Contributor

risicle commented Dec 1, 2022

Agreed. I think #150801 was the most recent attempt at this, which I think stalled a bit deciding whether we needed a nixos test for this and if we therefore needed a postgresqlJit package. I would personally vote yes on both counts, otherwise we'll never discover it breaking.

@risicle risicle mentioned this pull request Mar 4, 2023
12 tasks
@andir andir closed this Mar 6, 2023
Ma27 added a commit to Ma27/nixpkgs that referenced this pull request Mar 29, 2023
Closes NixOS#150801

Note: I decided against resuming directly on NixOS#150801 because the
conflict was too big (and resolving it seemed too error-prone to me).
Also the `this`-refactoring could be done in an easier manner, i.e. by
exposing JIT attributes with the correct configuration. More on that
below.

This patch creates variants of the `postgresql*`-packages with JIT[1]
support. Please note that a lot of the work was derived from previous
patches filed by other contributors, namely dasJ, andir and abbradar,
hence the co-authored-by tags below.

Effectively, the following things have changed:

* For JIT variants an LLVM-backed stdenv with clang is now used as
  suggested by dasJ[2]. We need LLVM and CLang[3] anyways to build the
  JIT-part, so no need to mix this up with GCC's stdenv. Also, using the
  `dev`-output of LLVM and clang's stdenv for building (and adding llvm
  libs as build-inputs) seems more cross friendly to me (which will
  become useful when cross-building for JIT-variants will actually be
  supported).

* Plugins inherit the build flags from the Makefiles in
  `$out/lib/pgxs/src` (e.g. `-Werror=unguarded-availability-new`). Since
  some of the flags are clang-specific (and stem from the use of the
  CLang stdenv) and don't work on gcc, the stdenv of `pkgs.postgresql`
  is passed to the plugins. I.e., plugins for non-JIT variants are built
  with a gcc stdenv on Linux and plugins for JIT variants with a clang
  stdenv.

  Since `plv8` hard-codes `gcc` as `$CC` in its Makefile[4], I marked it
  as broken for JIT-variants of postgresql only.

* Added a test-matrix to confirm that JIT works fine on each
  `pkgs.postgresql_*_jit` (thanks Andi for the original test in
  NixOS#124804!).

* For each postgresql version, a new attribute
  `postgresql_<version>_jit` (and a corresponding
  `postgresqlPackages<version>JitPackages`) are now exposed for better
  discoverability and prebuilt artifacts in the binary cache.

* In NixOS#150801 the `this`-argument was replaced by an internal recursion.
  I decided against this approach because it'd blow up the diff even
  more which makes the readability way harder and also harder to revert
  this if necessary.

  Instead, it is made sure that `this` always points to the correct
  variant of `postgresql` and re-using that in an additional
  `.override {}`-expression is trivial because the JIT-variant is
  exposed in `all-packages.nix`.

* I think the changes are sufficiently big to actually add myself as
  maintainer here.

* Added `libxcrypt` to `buildInputs` for versions <v13. While
  building things with an LLVM stdenv, these versions complained that
  the extern `crypt()` symbol can't be found. Not sure what this is
  exactly about, but since we want to switch to libxcrypt for `crypt()`
  usage anyways[5] I decided to add it. For >=13 it's not relevant
  anymore anyways[6].

* JIT support doesn't work with cross-compilation. It is attempted to
  build LLVM-bytecode (`%.bc` is the corresponding `make(1)`-rule) for
  each sub-directory in `backend/` for the JIT apparently, but with a
  $(CLANG) that can produce binaries for the build, not the host-platform.

  I managed to get a cross-build with JIT support working with
  `depsBuildBuild = [ llvmPackages.clang ] ++ buildInputs`, but
  considering that the resulting LLVM IR isn't platform-independent this
  doesn't give you much. In fact, I tried to test the result in a VM-test,
  but as soon as JIT was used to optimize a query, postgres would
  coredump with `Illegal instruction`.

A common concern of the original approach - with llvm as build input -
was the massive increase of closure size. With the new approach of using
the LLVM stdenv directly and patching out references to the clang drv in
`$out` the effective closure size changes are:

    $ nix path-info -Sh $(nix-build -A postgresql_14)
    /nix/store/kssxxqycwa3c7kmwmykwxqvspxxa6r1w-postgresql-14.7	306.4M
    $ nix path-info -Sh $(nix-build -A postgresql_14_jit)
    /nix/store/xc7qmgqrn4h5yr4vmdwy56gs4bmja9ym-postgresql-14.7	689.2M

Most of the increase in closure-size stems from the `lib`-output of
LLVM

    $ nix path-info -Sh /nix/store/5r97sbs5j6mw7qnbg8nhnq1gad9973ap-llvm-11.1.0-lib
    /nix/store/5r97sbs5j6mw7qnbg8nhnq1gad9973ap-llvm-11.1.0-lib	349.8M

which is why this shouldn't be enabled by default.

While this is quite much because of LLVM, it's still a massive
improvement over the simple approach of adding llvm/clang as
build-inputs and building with `--with-llvm`:

    $ nix path-info -Sh $(nix-build -E '
	with import ./. {};
	postgresql.overrideAttrs ({ configureFlags ? [], buildInputs ? [], ... }: {
	  configureFlags = configureFlags ++ [ "--with-llvm" ];
	  buildInputs = buildInputs ++ [ llvm clang ];
	})' -j0)
    /nix/store/i3bd2r21c6c3428xb4gavjnplfqxn27p-postgresql-14.7	  1.6G

Co-authored-by: Andreas Rammhold <andreas@rammhold.de>
Co-authored-by: Janne Heß <janne@hess.ooo>
Co-authored-by: Nikolay Amiantov <ab@fmap.me>

[1] https://www.postgresql.org/docs/current/jit-reason.html
[2] NixOS#124804 (comment)
    & NixOS#150801 (comment)
[3] This fails with the following error otherwise:
    ```
    configure: error: clang not found, but required when compiling --with-llvm, specify with CLANG=
    ```
[4] https://github.com/plv8/plv8/blob/v3.1.5/Makefile#L14
[5] NixOS#181764
[6] postgres/postgres@c45643d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants