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

llvm: make libLLVM.so instead of many .so files #12759

Closed
wants to merge 1 commit into from

Conversation

andrewrk
Copy link
Member

@andrewrk andrewrk commented Feb 2, 2016

Having many LLVM shared libraries makes code that links against
LLVM slow because the libraries needlessly communicate with each other
via extern symbols when the internal linking could have been static.

This change enables a setting which causes all the LLVM libraries to be statically linked together, and then into a final shared library, libLLVM.so. This is the strategy used, for example, in the arch package.

My motivation for this pull request is my own compiler project which is encumbered by the way LLVM is currently linked. Resolving dynamic symbols is currently a performance bottleneck.

However, this also affects other NixOS packages that depend on LLVM, such as rustc. Try running rustc --help and you will find it takes almost 100ms. Now compare that to cat --help which is almost instant. If you use callgrind to analyze what is taking the time, it's all in resolving the LLVM shared libs.

In fact now that I'm thinking about it, and I just tested it, the same is true about clang itself. The clang compiler spends a lot of time resolving dynamic symbols to LLVM which makes compile times slower.

I have not finished testing this change, but I want to open it up for discussion while doing so.

We have the option of leaving the .a files around for those who wish to link statically against LLVM, and deleting the .a files to force dependant packages to use libLLVM.so. In the former case this patch would slightly reduce disk usage, and in the latter case this patch would increase disk usage of the LLVM package by the binary size of all the .a files, which is about 450MB. My preference and the default option that happens if we take no action is the former - leave the .a files for those who want them. This is status quo for libclang.

Previously, this nixpkg deviated from the default build options by generating .so files instead of .a files. With this patch, the nixpkg will deviate from the default build options only by additionally generating libLLVM.so alongside the .a files.

In short, I expect this change to drastically speed up any program which depends on LLVM. The only challenge will be making sure that packages which depend on LLVM still build correctly.

cc @lovek323
cc @7c6f434c
cc @viric

@mention-bot
Copy link

By analyzing the blame information on this pull request, we identified @pikajude, @Ralith and @abbradar to be potential reviewers

Having many LLVM shared libraries makes code that links against
LLVM slow because the libraries needlessly communicate with each other
via extern symbols when the internal linking could have been static.
@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

OK, I haven't tested this in the nixpkgs ecosystem, but I verified that these cmake options indeed produce the desired output of a single libLLVM.so whose dynamic dependencies include only system dependencies, like these:

    linux-vdso.so.1 (0x00007fff7b528000)
    librt.so.1 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/librt.so.1 (0x00007f7413fe8000)
    libdl.so.2 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/libdl.so.2 (0x00007f7413de3000)
    libpthread.so.0 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/libpthread.so.0 (0x00007f7413bc6000)
    libm.so.6 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/libm.so.6 (0x00007f74138c3000)
    libstdc++.so.6 => /nix/store/5idnvskkm4hky2fj335xfbqpfim3g9pd-gcc-4.9.3/lib/libstdc++.so.6 (0x00007f74135b7000)
    libgcc_s.so.1 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/libgcc_s.so.1 (0x00007f74133a1000)
    libc.so.6 => /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib/libc.so.6 (0x00007f7413002000)
    /nix/store/n2wxp513rr00f6hr2dy0waqahns49dch-glibc-2.21/lib64/ld-linux-x86-64.so.2 (0x000055ca64c63000)

I'm currently building the package with my patch and will test if clang works successfully with these changes to the LLVM package.

@vcunat vcunat added 0.kind: enhancement Add something new 2.status: work-in-progress This PR isn't done labels Feb 2, 2016
@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

Increasing the size by 0.5 GB by default is no good, as e.g. mesa drivers depend on this and they're used by quite a lot of people (all intel for example).

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

Putting *.a into a separate output might be the best solution.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

Increasing the size by 0.5 GB by default is no good, as e.g. mesa drivers depend on this and they're used by quite a lot of people (all intel for example).

Can you explain the connection between the increase in size and the problem with depending on the package?

Also as a reminder, the option is still on the table to produce libLLVM.so and leave out the .a files.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

I built clang_37 with this patch, and indeed invoking clang is 8 times faster with the patch.

However I'm getting some linker errors in my project when trying to depend on LLVM now - let me resolve these before merging...

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

Also, I overestimated the addition in size. I was looking at a debug build, but the release build has these sizes:

  • all the .a files: 94.5 MB
  • libLLVM.so: 42.8 MB

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

OK, as far as I'm concerned, this patch is ready for review. It works for my use case and it builds clang successfully. Provided that the rest of the ecosystem builds, this patch is my proposal.

This is a huge performance improvement for anything that relies on LLVM.

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

Can you explain the connection between the increase in size and the problem with depending on the package?

They pull libLLVM into the runtime closure, i.e. everyone using mesa drivers will have those files.

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

What is "-DLLVM_DYLIB_EXPORT_ALL=ON" for? I haven't found it in the docs.

@vcunat vcunat added the 1.severity: mass-rebuild This PR causes a large number of packages to rebuild label Feb 2, 2016
@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

What is "-DLLVM_DYLIB_EXPORT_ALL=ON" for? I haven't found it in the docs.

From CMakeLists.txt:

option(LLVM_DYLIB_EXPORT_ALL "Export all symbols from libLLVM.dylib (default is C API only" OFF)

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

Those docs are out of date. They list another option, LLVM_LINK_LLVM_DYLIB which is no longer present in 3.7.1. So, it seems the canonical source of knowledge is CMakeLists.txt itself.

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

Mesa has a problem with this:

configure: error: Could not find llvm shared libraries:
        Please make sure you have built llvm with the --enable-shared option
        and that your llvm libraries are installed in /nix/store/ibghiccbckpi2ppj4j0c33hxbw9wzvqp-llvm-3.7.1/lib
        If you have installed your llvm libraries to a different directory you
        can use the --with-llvm-prefix= configure flag to specify this directory.
        NOTE: Mesa is attempting to use llvm shared libraries by default.
        If you do not want to build with llvm shared libraries and instead want to
        use llvm static libraries then add --disable-llvm-shared-libs to your configure
        invocation and rebuild.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

Mesa will perform better if it links against libLLVM.so instead of individual shared libraries. It will also perform better if it statically links against the .a files, but the tradeoff there is +95MB.

Statically linking sounds nice to me, but if for some reason the extra file size is not acceptable, then I think it would be worth it to try to get the mesa package to work with libLLVM.so.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

Arch linux patches the configure file to replace the other .so files to use libLLVM.so:

https://projects.archlinux.org/svntogit/packages.git/tree/trunk/PKGBUILD?h=packages/mesa#n26

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

As far as I can tell, Debian builds mesa against LLVM static libraries: https://anonscm.debian.org/cgit/pkg-xorg/lib/mesa.git/tree/debian/rules

@andrewrk
Copy link
Member Author

andrewrk commented Feb 2, 2016

I think trying to resist static linking against LLVM is an uphill battle; static libraries are the default output of LLVM and static libraries are the default input of Mesa.

If nixos makes the decision to do that battle, however, then we should do it right and use libLLVM.so instead of the many other libs which kills performance.

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

I think that battle is fought by many distros. Debian-based systems use mesa linked against libLLVM-*.so.

@vcunat
Copy link
Member

vcunat commented Feb 2, 2016

I expect the main problem is that too many separate mesa libs need to link against LLVM.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 4, 2016

I've been using clang with this patch in a nix-shell for the past couple of days, and it is an order of magnitude faster than before. I understand it's tricky to fundamentally change the way LLVM is linked, but we can't pass up a performance improvement like this. We need to make this happen.

@vcunat
Copy link
Member

vcunat commented Feb 4, 2016

Nothing really prevents us from using a differently set up llvm for mesa than for other packages. (That could also avoid the mass rebuild.)

@edolstra
Copy link
Member

edolstra commented Feb 4, 2016

Maybe libLLVM.so could be placed in a separate output?

However, looking at http://hydra.nixos.org/build/30758676/contents/1, it appears that we don't actually build static libraries for llvm anymore. So moving libLLVM.so into a separate output would only save a couple dozen megabytes from the runtime closure (basically, the bin/ and include/ directories).

@vcunat
Copy link
Member

vcunat commented Feb 4, 2016

This PR enables static libraries which makes the result rather large IIRC. (The main problem might be that the tools get statically linked.)

@andrewrk
Copy link
Member Author

andrewrk commented Feb 4, 2016

As is, this PR enables static libraries. I personally think having the static libraries is nice, but we also have the option of not shipping the static libraries and have the package only export the single .so file. Then we have the job of making sure that every package which depends on LLVM knows how to build against the single .so instead of many.

@andrewrk
Copy link
Member Author

andrewrk commented Feb 5, 2016

FWIW, I tested the mesa-noglu package with --disable-llvm-shared-libs and it works fine.

I looked at the output size and I'm honestly shocked at how small it is:

andy@nixos:~/dev/tetris$ ls -ahl /nix/store/hk00pskjnhllqqvsj0wmpiyaqf7x27cw-mesa-noglu-11.0.8/lib/
total 1.3M
dr-xr-xr-x 3 root nixbld 4.0K Dec 31  1969 .
dr-xr-xr-x 6 root nixbld 4.0K Dec 31  1969 ..
-r-xr-xr-x 1 root nixbld 2.6K Dec 31  1969 libEGL.la
lrwxrwxrwx 1 root nixbld   15 Dec 31  1969 libEGL.so -> libEGL.so.1.0.0
lrwxrwxrwx 1 root nixbld   15 Dec 31  1969 libEGL.so.1 -> libEGL.so.1.0.0
-r-xr-xr-x 1 root nixbld 192K Dec 31  1969 libEGL.so.1.0.0
-r-xr-xr-x 1 root nixbld 1.6K Dec 31  1969 libgbm.la
lrwxrwxrwx 1 root nixbld   15 Dec 31  1969 libgbm.so -> libgbm.so.1.0.0
lrwxrwxrwx 1 root nixbld   15 Dec 31  1969 libgbm.so.1 -> libgbm.so.1.0.0
-r-xr-xr-x 1 root nixbld  67K Dec 31  1969 libgbm.so.1.0.0
-r-xr-xr-x 1 root nixbld  983 Dec 31  1969 libglapi.la
lrwxrwxrwx 1 root nixbld   17 Dec 31  1969 libglapi.so -> libglapi.so.0.0.0
lrwxrwxrwx 1 root nixbld   17 Dec 31  1969 libglapi.so.0 -> libglapi.so.0.0.0
-r-xr-xr-x 1 root nixbld 247K Dec 31  1969 libglapi.so.0.0.0
-r-xr-xr-x 1 root nixbld 1.3K Dec 31  1969 libGLESv1_CM.la
lrwxrwxrwx 1 root nixbld   21 Dec 31  1969 libGLESv1_CM.so -> libGLESv1_CM.so.1.1.0
lrwxrwxrwx 1 root nixbld   21 Dec 31  1969 libGLESv1_CM.so.1 -> libGLESv1_CM.so.1.1.0
-r-xr-xr-x 1 root nixbld  23K Dec 31  1969 libGLESv1_CM.so.1.1.0
-r-xr-xr-x 1 root nixbld 1.2K Dec 31  1969 libGLESv2.la
lrwxrwxrwx 1 root nixbld   18 Dec 31  1969 libGLESv2.so -> libGLESv2.so.2.0.0
lrwxrwxrwx 1 root nixbld   18 Dec 31  1969 libGLESv2.so.2 -> libGLESv2.so.2.0.0
-r-xr-xr-x 1 root nixbld  39K Dec 31  1969 libGLESv2.so.2.0.0
-r-xr-xr-x 1 root nixbld 3.3K Dec 31  1969 libGL.la
lrwxrwxrwx 1 root nixbld   14 Dec 31  1969 libGL.so -> libGL.so.1.2.0
lrwxrwxrwx 1 root nixbld   14 Dec 31  1969 libGL.so.1 -> libGL.so.1.2.0
-r-xr-xr-x 1 root nixbld 671K Dec 31  1969 libGL.so.1.2.0
-r-xr-xr-x 1 root nixbld 1000 Dec 31  1969 libwayland-egl.la
lrwxrwxrwx 1 root nixbld   23 Dec 31  1969 libwayland-egl.so -> libwayland-egl.so.1.0.0
lrwxrwxrwx 1 root nixbld   23 Dec 31  1969 libwayland-egl.so.1 -> libwayland-egl.so.1.0.0
-r-xr-xr-x 1 root nixbld 7.1K Dec 31  1969 libwayland-egl.so.1.0.0
dr-xr-xr-x 2 root nixbld 4.0K Dec 31  1969 pkgconfig
--- a/pkgs/development/libraries/mesa/default.nix
+++ b/pkgs/development/libraries/mesa/default.nix
@@ -95,7 +95,7 @@ stdenv.mkDerivation {
     "--with-egl-platforms=x11,wayland,drm"

     "--enable-gallium-llvm"
-    "--enable-llvm-shared-libs"
+    "--disable-llvm-shared-libs"
   ] ++ optional enableTextureFloats "--enable-texture-float"
     ++ optional grsecEnabled "--enable-glx-rts"; # slight performance degradation, enable only for grsec

@andrewrk
Copy link
Member Author

andrewrk commented Feb 5, 2016

Hmm maybe those weren't the correct output files. Maybe these:

andy@nixos:~/dev/tetris$ ls -alh /nix/store/z8biwcdy7vsdkchx7lhgiq5x65lw8fxw-mesa-noglu-11.0.8-osmesa/lib/
total 23M
dr-xr-xr-x 3 root nixbld 4.0K Dec 31  1969 .
dr-xr-xr-x 3 root nixbld 4.0K Dec 31  1969 ..
-r-xr-xr-x 1 root nixbld 1.8K Dec 31  1969 libOSMesa.la
lrwxrwxrwx 1 root nixbld   18 Dec 31  1969 libOSMesa.so -> libOSMesa.so.8.0.0
lrwxrwxrwx 1 root nixbld   18 Dec 31  1969 libOSMesa.so.8 -> libOSMesa.so.8.0.0
-r-xr-xr-x 1 root nixbld  23M Dec 31  1969 libOSMesa.so.8.0.0
dr-xr-xr-x 2 root nixbld 4.0K Dec 31  1969 pkgconfig
andy@nixos:~/dev/tetris$ ls -ahl /nix/store/52vyzybqy29z6bjaabbkc43cbdlvs987-mesa-noglu-11.0.8-drivers/lib/
total 161M
dr-xr-xr-x 5 root nixbld 4.0K Dec 31  1969 .
dr-xr-xr-x 3 root nixbld 4.0K Dec 31  1969 ..
dr-xr-xr-x 2 root nixbld 4.0K Dec 31  1969 d3d
dr-xr-xr-x 2 root nixbld 4.0K Dec 31  1969 dri
lrwxrwxrwx 1 root nixbld   25 Dec 31  1969 libvdpau_nouveau.so -> libvdpau_nouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   25 Dec 31  1969 libvdpau_nouveau.so.1 -> libvdpau_nouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   25 Dec 31  1969 libvdpau_nouveau.so.1.0 -> libvdpau_nouveau.so.1.0.0
-r-xr-xr-x 1 root nixbld  25M Dec 31  1969 libvdpau_nouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r300.so -> libvdpau_r300.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r300.so.1 -> libvdpau_r300.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r300.so.1.0 -> libvdpau_r300.so.1.0.0
-r-xr-xr-x 1 root nixbld  25M Dec 31  1969 libvdpau_r300.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r600.so -> libvdpau_r600.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r600.so.1 -> libvdpau_r600.so.1.0.0
lrwxrwxrwx 1 root nixbld   22 Dec 31  1969 libvdpau_r600.so.1.0 -> libvdpau_r600.so.1.0.0
-r-xr-xr-x 1 root nixbld  25M Dec 31  1969 libvdpau_r600.so.1.0.0
lrwxrwxrwx 1 root nixbld   26 Dec 31  1969 libvdpau_radeonsi.so -> libvdpau_radeonsi.so.1.0.0
lrwxrwxrwx 1 root nixbld   26 Dec 31  1969 libvdpau_radeonsi.so.1 -> libvdpau_radeonsi.so.1.0.0
lrwxrwxrwx 1 root nixbld   26 Dec 31  1969 libvdpau_radeonsi.so.1.0 -> libvdpau_radeonsi.so.1.0.0
-r-xr-xr-x 1 root nixbld  25M Dec 31  1969 libvdpau_radeonsi.so.1.0.0
-r-xr-xr-x 1 root nixbld 2.3K Dec 31  1969 libxatracker.la
lrwxrwxrwx 1 root nixbld   21 Dec 31  1969 libxatracker.so -> libxatracker.so.2.2.0
lrwxrwxrwx 1 root nixbld   21 Dec 31  1969 libxatracker.so.2 -> libxatracker.so.2.2.0
-r-xr-xr-x 1 root nixbld  22M Dec 31  1969 libxatracker.so.2.2.0
lrwxrwxrwx 1 root nixbld   23 Dec 31  1969 libXvMCnouveau.so -> libXvMCnouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   23 Dec 31  1969 libXvMCnouveau.so.1 -> libXvMCnouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   23 Dec 31  1969 libXvMCnouveau.so.1.0 -> libXvMCnouveau.so.1.0.0
-r-xr-xr-x 1 root nixbld  22M Dec 31  1969 libXvMCnouveau.so.1.0.0
lrwxrwxrwx 1 root nixbld   20 Dec 31  1969 libXvMCr600.so -> libXvMCr600.so.1.0.0
lrwxrwxrwx 1 root nixbld   20 Dec 31  1969 libXvMCr600.so.1 -> libXvMCr600.so.1.0.0
lrwxrwxrwx 1 root nixbld   20 Dec 31  1969 libXvMCr600.so.1.0 -> libXvMCr600.so.1.0.0
-r-xr-xr-x 1 root nixbld  22M Dec 31  1969 libXvMCr600.so.1.0.0
dr-xr-xr-x 2 root nixbld 4.0K Dec 31  1969 pkgconfig

@andrewrk
Copy link
Member Author

andrewrk commented Feb 5, 2016

These file sizes all seem reasonable to me.

@vcunat
Copy link
Member

vcunat commented Feb 6, 2016

No, the sizes aren't reasonable at all. A year ago mesa_drivers + llvm had ~60 + 50 MB, now it's ~180 + 70 MB, and after this PR it would get ~540 MB without runtime dependence on llvm (of size ~470 MB).

http://hydra.nixos.org/job/nixos/trunk-combined/nixpkgs.mesa_drivers.x86_64-linux#tabs-charts
http://hydra.nixos.org/job/nixos/trunk-combined/nixpkgs.llvm.x86_64-linux#tabs-charts

@andrewrk
Copy link
Member Author

andrewrk commented Mar 8, 2016

LLVM 3.8.0 is released: http://llvm.org/releases/3.8.0/docs/ReleaseNotes.html

From the release notes:

Optional support for linking clang and the LLVM tools with a single libLLVM shared library. To enable this, pass -DLLVM_LINK_LLVM_DYLIB=ON to CMake. See Building LLVM with CMake for more details.

Perhaps in the llvm_38 package we could enable this option?

@vcunat
Copy link
Member

vcunat commented Mar 8, 2016

Yes, that sounds promising.

This was referenced Mar 9, 2016
vcunat pushed a commit that referenced this pull request Mar 28, 2016
vcunat's review:
 - let's not switch the default versions of llvm* for now
 - the only changes I see is adding python to clang's buildInputs
   and using the big so-file as discussed in #12759
   (BUILD_SHARED_LIBS -> LLVM_LINK_LLVM_DYLIB)
 - in future it will be nice to split libLLVM into a separate output
vcunat pushed a commit that referenced this pull request Mar 28, 2016
vcunat's review:
 - let's not switch the default versions of llvm* for now
 - the only changes I see is adding python to clang's buildInputs
   and using the big so-file as discussed in #12759
   (BUILD_SHARED_LIBS -> LLVM_LINK_LLVM_DYLIB)
 - in future it will be nice to split libLLVM into a separate output

(cherry picked from commit f5fe051)
@andrewrk andrewrk mentioned this pull request Apr 24, 2016
7 tasks
@andrewrk
Copy link
Member Author

andrewrk commented May 1, 2016

This is fixed as of llvm 3.8.0 package.

@andrewrk andrewrk closed this May 1, 2016
adrianpk added a commit to adrianpk/nixpkgs that referenced this pull request May 31, 2024
vcunat's review:
 - let's not switch the default versions of llvm* for now
 - the only changes I see is adding python to clang's buildInputs
   and using the big so-file as discussed in NixOS#12759
   (BUILD_SHARED_LIBS -> LLVM_LINK_LLVM_DYLIB)
 - in future it will be nice to split libLLVM into a separate output

(cherry picked from commit f5fe051)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.kind: enhancement Add something new 1.severity: mass-rebuild This PR causes a large number of packages to rebuild 2.status: work-in-progress This PR isn't done
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants